Java中的原始类型是什么?为什么我经常听到不应该在新代码中使用它们?
原始类型是Java语言的古老历史。 一开始有集合
,它们包含对象
,不多也不少。 对集合
的每个操作都需要将对象
强制转换为所需类型。
List aList = new ArrayList();
String s = "Hello World!";
aList.add(s);
String c = (String)aList.get(0);
虽然这在大多数情况下是有效的,但还是发生了错误
List aNumberList = new ArrayList();
String one = "1";//Number one
aNumberList.add(one);
Integer iOne = (Integer)aNumberList.get(0);//Insert ClassCastException here
旧的无类型集合不能强制执行类型安全,因此程序员必须记住他在集合中存储的内容。
泛型是为了绕过这一限制而发明的,开发人员只声明一次存储的类型,编译器将代之以声明。
List<String> aNumberList = new ArrayList<String>();
aNumberList.add("one");
Integer iOne = aNumberList.get(0);//Compile time error
String sOne = aNumberList.get(0);//works fine
供比较:
// Old style collections now known as raw types
List aList = new ArrayList(); //Could contain anything
// New style collections with Generics
List<String> aList = new ArrayList<String>(); //Contains only Strings
更复杂的可比较接口:
//raw, not type save can compare with Other classes
class MyCompareAble implements CompareAble
{
int id;
public int compareTo(Object other)
{return this.id - ((MyCompareAble)other).id;}
}
//Generic
class MyCompareAble implements CompareAble<MyCompareAble>
{
int id;
public int compareTo(MyCompareAble other)
{return this.id - other.id;}
}
注意,不可能用具有原始类型的compareable
实现compareable
接口。 为什么不应该使用它们:
集合
中的任何对象
必须强制转换后才能使用对象
编译器的作用:泛型是向后兼容的,它们使用与原始类型相同的Java类。 这种神奇的情况主要发生在编译时。
List<String> someStrings = new ArrayList<String>();
someStrings.add("one");
String one = someStrings.get(0);
将编译为:
List someStrings = new ArrayList();
someStrings.add("one");
String one = (String)someStrings.get(0);
这与直接使用原始类型时编写的代码相同。 虽然我不确定Compareable
接口会发生什么,但我猜它创建了两个CompareTo
函数,一个接受MyCompareable
,另一个接受Object
,并在强制转换后将其传递给第一个函数。
原始类型的替代方法是什么:使用泛型
原始类型是没有任何类型参数的泛型类或接口的名称。 例如,给定泛型Box类:
public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}
要创建框
的参数化类型,请为正式类型参数t
提供一个实际类型参数:
Box<Integer> intBox = new Box<>();
如果省略实际类型参数,则创建框
的原始类型:
Box rawBox = new Box();
因此,box
是泛型类型box
的原始类型。 但是,非泛型类或接口类型不是原始类型。
原始类型会出现在遗留代码中,因为在JDK5.0之前,很多API类(如集合,类)都不是通用的。 当使用原始类型时,您基本上获得了泛型前的行为-框
给您提供了对象
。 为了向后兼容,允许将参数化类型分配给其原始类型:
Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OK
但如果将原始类型分配给参数化类型,则会收到警告:
Box rawBox = new Box(); // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox; // warning: unchecked conversion
如果使用原始类型调用在相应泛型类型中定义的泛型方法,还会收到警告:
Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8); // warning: unchecked invocation to set(T)
警告显示原始类型绕过泛型类型检查,将不安全代码的捕获延迟到运行时。 因此,您应该避免使用原始类型。
类型擦除部分有关于Java编译器如何使用原始类型的更多信息。
如前所述,当将遗留代码与泛型代码混合使用时,您可能会遇到类似以下的警告消息:
注意:example.java使用未经检查或不安全的操作。
注意:有关详细信息,请使用-xlint:unchecked重新编译。
当使用对原始类型进行操作的较旧API时,可能会发生这种情况,如以下示例所示:
public class WarningDemo {
public static void main(String[] args){
Box<Integer> bi;
bi = createBox();
}
static Box createBox(){
return new Box();
}
}
术语“未检查”是指编译器没有足够的类型信息来执行确保类型安全所必需的所有类型检查。 默认情况下,“unchecked”警告是禁用的,尽管编译器会给出提示。 要查看所有“未检查”警告,请使用-xlint:unchecked重新编译。
用-xlint:unchecked重新编译前面的示例会发现以下附加信息:
WarningDemo.java:4: warning: [unchecked] unchecked conversion
found : Box
required: Box<java.lang.Integer>
bi = createBox();
^
1 warning
要完全禁用未检查的警告,请使用-xlint:-unchecked标志。 @suppresswarnings(“unchecked”)
批注取消未检查的警告。 如果您不熟悉@suppresswarnings
语法,请参阅注释。
原文来源:Java教程