问题:

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


当前回答

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

“原始类型”是使用泛型类而不为其参数化类型指定类型参数,例如使用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

其他回答

编译器希望您编写以下内容:

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

因为否则,您可以将任何您喜欢的类型添加到列表中,使实例化为新的ArrayList<String>()变得毫无意义。Java泛型只是一个编译时特性,因此,如果将新的ArrayList<String>()创建的对象分配给“原始类型”List的引用,它将欣然接受Integer或JFrame元素-对象本身不知道应该包含什么类型,只有编译器知道。

这是另一种生肉会咬你的情况:

public class StrangeClass<T> {
  @SuppressWarnings("unchecked")
  public <X> X getSomethingElse() {
    return (X)"Testing something else!";
  }

  public static void main(String[] args) {
    final StrangeClass<String> withGeneric    = new StrangeClass<>();
    final StrangeClass         withoutGeneric = new StrangeClass();
    final String               value1,
                               value2;

    // Compiles
    value1 = withGeneric.getSomethingElse();

    // Produces compile error:
    // incompatible types: java.lang.Object cannot be converted to java.lang.String
    value2 = withoutGeneric.getSomethingElse();
  }
}

这是反直觉的,因为您希望原始类型只影响绑定到类类型参数的方法,但实际上它也影响具有自己类型参数的泛型方法。

正如在公认的答案中所提到的,您将失去对原始类型代码中泛型的所有支持。每个类型参数都转换为其擦除(在上面的示例中,它只是Object)。

避免使用原始类型。

原始类型是指在不指定类型参数的情况下使用泛型类型。

例如:

列表是原始类型,而list<String>是参数化类型。

当JDK1.5中引入泛型时,原始类型仅被保留以保持与旧版本Java的向后兼容性。

尽管仍然可以使用原始类型,但应避免使用:

他们通常需要石膏。它们不是类型安全的,一些重要类型的错误只会在运行时出现。它们的表达力较低,并且不像参数化类型那样自我文档化。。

例子:

import java.util.*;
public final class AvoidRawTypes {
    void withRawType() {
        //Raw List doesn't self-document, 
        //doesn't state explicitly what it can contain
        List stars = Arrays.asList("Arcturus", "Vega", "Altair");
        Iterator iter = stars.iterator();
        while (iter.hasNext()) {
            String star = (String) iter.next(); //cast needed
            log(star);
        }
    }

    void withParameterizedType() {
        List < String > stars = Arrays.asList("Spica", "Regulus", "Antares");
        for (String star: stars) {
            log(star);
        }
    }

    private void log(Object message) {
        System.out.println(Objects.toString(message));
    }
}
 

供参考:https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html

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

例如,取消序列化函数可能返回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;
}