什么是原始类型?
Java语言规范对原始类型的定义如下:
JLS 4.8原始类型
原始类型定义为以下之一:
通过采用通用类型声明的名称而没有随附的类型参数列表形成的引用类型。
数组类型,其元素类型为原始类型。
未从的超类或超接口继承static的原始类型的非成员类型。RR
这是一个例子说明:
public class MyType<E> {
class Inner { }
static class Nested { }
public static void main(String[] args) {
MyType mt; // warning: MyType is a raw type
MyType.Inner inn; // warning: MyType.Inner is a raw type
MyType.Nested nest; // no warning: not parameterized type
MyType<Object> mt1; // no warning: type parameter given
MyType<?> mt2; // no warning: type parameter given (wildcard OK!)
}
}
这MyType<E>
是参数化类型(JLS 4.5)。通常,通俗地简称MyType为这种类型是很常见的,但是从技术上来说,名称是MyType<E>
。
mt在上述定义的第一个要点之前具有原始类型(并生成编译警告);inn在第三个要点之前也具有原始类型。
MyType.Nested即使是参数化类型的成员类型,它也不是参数化类型MyType
mt1和mt2都是使用实际类型参数声明的,因此它们不是原始类型。
原始类型有何特别之处?
本质上,原始类型的行为与引入泛型之前的行为相同。也就是说,以下在编译时完全合法。
List names = new ArrayList(); // warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // not a compilation error!
上面的代码运行得很好,但是假设您还具有以下内容:
for (Object o : names) {
String name = (String) o;
System.out.println(name);
} // throws ClassCastException!
// java.lang.Boolean cannot be cast to java.lang.String
现在,我们在运行时遇到了麻烦,因为names其中包含的不是instanceof String。
据推测,如果你想names仅仅是遏制String
,您可以或许仍然使用原始型和手动检查每一个 add自己,然后手动施放到String
每一个项目从names
。更好的是,尽管不要使用原始类型,而让编译器利用Java泛型的强大功能为您完成所有工作。
List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!
当然,如果你不要想names允许Boolean,那么你可以将它声明为List<Object> names
,和上面的代码将编译。
原始类型与
以下是有效Java 2nd Edition条款23的引用:不要在新代码中使用原始类型:
原始类型List和参数化类型之间有什么区别
List<Object>
?松散地说,前者选择了泛型类型检查,而后者则明确告诉编译器它能够保存任何类型的对象。虽然您可以将a传递List<String>
给类型的参数List,但是不能将其传递给type的参数List<Object>
。泛型有子类型化规则,并且List<String>
是原始类型的子类型List,但不是参数化类型的子类型List<Object>
。结果,如果您使用原始类型如ListList<Object>
,则会失去类型安全性,但是如果您使用诸如参数的类型,则不会失去类型安全性。
为了说明这一点,请考虑以下方法,该方法采用aList<Object>
并附加a new Object()。
void appendNewObject(List<Object> list) {
list.add(new Object());
}
Java中的泛型是不变的。AList<String>
不是a List<Object>
,因此以下将生成编译器警告:
List<String> names = new ArrayList<String>();
appendNewObject(names); // compilation error!
如果您声明appendNewObject将原始类型List作为参数,那么它将进行编译,因此您将失去从泛型获得的类型安全性。
原始类型与<?>用作类型参数有何不同?
List<Object>
,List<String>
等等都是List<?>
,所以说它们只是List
替代品可能会很诱人。但是,有一个主要区别:由于List<E>
仅定义add(E)
,因此您不能仅将任意对象添加到List<?>
。另一方面,由于原始类型List
没有类型安全性,所以您几乎可以add
对a进行任何操作List
。
请考虑以下片段的以下变体:
static void appendNewObject(List<?> list) {
list.add(new Object()); // compilation error!
}
//...
List<String> names = new ArrayList<String>();
appendNewObject(names); // this part is fine!
编译器做了出色的工作,保护您避免潜在地违反List<?>
!的类型不变性。如果已将参数声明为原始类型List
list
,则代码将编译,并且违反的类型不变式List<String> names
。
原始类型是该类型的擦除
返回JLS 4.8:
这是可能作为一种类型使用擦除参数化类型或数组类型,其元素类型是参数化类型的擦除。这种类型称为原始类型。
[…]
原始类型的超类(分别是超接口)是对泛型类型的任何参数化的超类(超接口)的擦除。
构造函数,实例方法或未从其超类或超接口继承static
的原始类型的非字段的C
类型是与在对应的通用声明中擦除其类型相对应的原始类型C
。
简单来说,当使用原始类型时,构造函数,实例方法和非static字段也将被擦除。
请看以下示例:
class MyType<E> {
List<String> getNames() {
return Arrays.asList("John", "Mary");
}
public static void main(String[] args) {
MyType rawType = new MyType();
// unchecked warning!
// required: List<String> found: List
List<String> names = rawType.getNames();
// compilation error!
// incompatible types: Object cannot be converted to String
for (String str : rawType.getNames())
System.out.print(str);
}
}
当我们使用raw时MyType
,getNames
也会被擦除,因此它返回raw List
!
JLS 4.6继续解释以下内容:
类型擦除还将映射构造函数或方法的签名到没有参数化类型或类型变量的签名。删除构造函数或方法签名s是一种签名,该签名s与中所给定的所有正式参数类型的名称相同,并且也删除了其中提供的所有形式参数类型s。
如果擦除方法或构造函数的签名,则方法的返回类型以及通用方法或构造函数的类型参数也会被擦除。
通用方法签名的擦除没有类型参数。
以下错误报告包含编译器开发人员Maurizio Cimadamore和JLS的作者之一Alex Buckley关于为何应发生这种行为的一些想法:https : //bugs.openjdk.java.net/browse / JDK-6400189。(简而言之,它使规范更简单。)
如果不安全,为什么允许使用原始类型?
这是JLS 4.8的另一句话:
仅允许使用原始类型作为对遗留代码兼容性的让步。强烈建议不要在将通用性引入Java编程语言后在代码中使用原始类型。Java编程语言的未来版本可能会禁止使用原始类型。
有效的Java 2nd Edition也要添加以下内容:
鉴于您不应该使用原始类型,语言设计者为何允许它们?提供兼容性。
引入泛型后,Java平台即将进入第二个十年,并且存在大量不使用泛型的Java代码。至关重要的是,所有这些代码都必须合法并可以与使用泛型的新代码互操作。将参数化类型的实例传递给设计用于普通类型的方法必须合法,反之亦然。这项要求称为迁移兼容性,因此决定支持原始类型。
总而言之,绝对不要在新代码中使用原始类型。您应该始终使用参数化类型。
有没有例外?
不幸的是,由于Java泛型是非泛型的,因此在新代码中必须使用原始类型有两个例外:
List.class,notList<String>.class
instanceof
操作数,例如o instanceof Set
,不是o instanceof Set<String>
问题内容: Java中的原始类型是什么? 基本类型和引用类型之间有什么区别? Java有多少种原始类型,它们是什么? 问题答案: 在Java中,每个变量都有在源代码中声明的类型。类型有两种:引用类型和原始类型。引用类型是对对象的引用。基本类型直接包含值。有8种原始类型: *Byte *Short *Integer *Long *Scorch *Floating *Double *Boolean v
我在Eclipse Mars中写了这行代码,以达到混乱的目的: 我收到以下编译器错误消息: 无法在原始类型null上调用toString() 这很奇怪,因为不是一个基元类型,也不是一个对象引用,如下所述:null是一个对象吗? 所以,为了确保万无一失,我尝试使用< code>javac编译这样奇怪的代码行,得到了这样的结果: 有人知道为什么Eclipse会给出这样(IMO)误导性的编译器错误消息吗
问题内容: 为什么大多数其他数据类型都没有Java的String基本类型? 问题答案: 字符串是一个对象,根本不是原始类型,只是一个字符数组。James Gosling的访谈 摘录摘述了Java中根本存在原始类型的原因,这很有趣。 Bill Venners: Java为什么会有原始类型?为什么不是所有事物都只是一个对象? James Gosling: 完全是效率问题。有各种各样的人已经建立了以in
这两条线有什么区别 和 或者
问题内容: Java中的原始类型是什么?为什么我经常听到不应该在新代码中使用它们的信息? 如果我们不能使用原始类型,那有什么选择呢?有什么更好的选择? 问题答案: 什么是原始类型? Java语言规范对原始类型的定义如下: JLS 4.8原始类型 原始类型定义为以下之一: 通过采用通用类型声明的名称而没有随附的类型参数列表而形成的引用类型。 数组类型,其元素类型为原始类型。 未从的超类或超接口继承s
问题内容: 在Java中,我们可以直接用于声明字符串变量名称并指定其值。即使String是非原始数据类型,也不必通过使用new关键字将字符串定义为数组。 有人可以解释为什么String是非原始数据类型吗? 问题答案: 这是字符串文字。像这样声明字符串时,实际上是在String上调用intern()方法。此方法引用内部字符串对象池。如果已经存在一个字符串值“ This is stringlitera