问题:

Java中的原始类型是什么?为什么我经常听到不应该在新代码中使用它们?如果我们不能使用原始类型,还有什么替代方法?如何更好?


当前回答

教程页面。

原始类型是没有任何类型参数的泛型类或接口的名称。例如,给定泛型Box类:

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

要创建Box的参数化类型,请为正式类型参数T提供一个实际的类型参数:

Box<Integer> intBox = new Box<>();

如果省略了实际类型参数,则创建原始类型Box:

Box rawBox = new Box();

其他回答

 private static List<String> list = new ArrayList<String>();

您应该指定类型参数。

警告建议,应将定义为支持泛型的类型参数化,而不是使用其原始形式。

List被定义为支持泛型:公共类List<E>。这允许在编译时检查许多类型安全操作。

什么是原始类型,为什么我经常听到不应该在新代码中使用它们?

“原始类型”是使用泛型类而不为其参数化类型指定类型参数,例如使用List而不是List<String>。当泛型引入Java时,几个类被更新为使用泛型。使用这些类作为“原始类型”(不指定类型参数)允许遗留代码仍然编译。

“原始类型”用于向后兼容。不建议在新代码中使用它们,因为使用带有类型参数的泛型类可以实现更强的类型,这反过来可能会提高代码的可理解性,并导致更早地发现潜在问题。

如果我们不能使用原始类型,还有什么替代方法?如何更好?

首选的替代方法是按预期使用泛型类-带有适当的类型参数(例如List<String>)。这允许程序员更具体地指定类型,向未来的维护人员传达关于变量或数据结构的预期用途的更多含义,并允许编译器强制执行更好的类型安全性。这些优点一起可以提高代码质量,并有助于防止引入一些编码错误。

例如,对于程序员希望确保名为“names”的List变量仅包含字符串的方法:

List<String> names = new ArrayList<String>();
names.add("John");          // OK
names.add(new Integer(1));  // compile error

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

Comparable接口更复杂:

//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;}
}

请注意,无法使用原始类型的compareTo(MyCompareAble)实现CompareAbble接口。为什么不应该使用它们:

存储在集合中的任何对象都必须在使用之前进行强制转换使用泛型启用编译时检查使用原始类型与将每个值存储为Object相同

编译器的作用:泛型是向后兼容的,它们使用与原始类型相同的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函数,一个接受MyCompareAbl,另一个接受Object并在强制转换后将其传递给第一个。

原始类型的替代方法是什么:使用泛型

当原始类型表达您想要表达的内容时,它们是很好的。

例如,取消序列化函数可能返回List,但它不知道列表的元素类型。所以List是这里合适的返回类型。

简单综合一下:原始类型是一个没有类型参数的泛型类型(例如:List是List<E>的原始类型),不应该使用原始类型。它们的存在是为了与旧版本的Java兼容。我们希望尽快(编译时)发现错误,使用原始类型可能会在运行时导致错误。在两种情况下,我们仍然需要使用原始类型:

类文字的用法(List.class)instanceof的用法

示例:

//Use of raw type : don't !
private final Collection stamps = ...
stamps.add(new Coin(...)); //Erroneous insertion. Does not throw any error
Stamp s = (Stamp) stamps.get(i); // Throws ClassCastException when getting the Coin

//Common usage of instance of
if (o instanceof Set){
    Set<?> = (Set<?>) o;
}