问题:

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


当前回答

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

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,但它不知道列表的元素类型。所以List是这里合适的返回类型。

我在做了一些示例练习后发现了这一页,并有着完全相同的困惑。

===============我根据示例提供的代码===============

public static void main(String[] args) throws IOException {

    Map wordMap = new HashMap();
    if (args.length > 0) {
        for (int i = 0; i < args.length; i++) {
            countWord(wordMap, args[i]);
        }
    } else {
        getWordFrequency(System.in, wordMap);
    }
    for (Iterator i = wordMap.entrySet().iterator(); i.hasNext();) {
        Map.Entry entry = (Map.Entry) i.next();
        System.out.println(entry.getKey() + " :\t" + entry.getValue());
    }

=========================到此代码========================

public static void main(String[] args) throws IOException {
    // replace with TreeMap to get them sorted by name
    Map<String, Integer> wordMap = new HashMap<String, Integer>();
    if (args.length > 0) {
        for (int i = 0; i < args.length; i++) {
            countWord(wordMap, args[i]);
        }
    } else {
        getWordFrequency(System.in, wordMap);
    }
    for (Iterator<Entry<String, Integer>> i = wordMap.entrySet().iterator(); i.hasNext();) {
        Entry<String, Integer> entry =   i.next();
        System.out.println(entry.getKey() + " :\t" + entry.getValue());
    }

}

===============================================================================

这可能更安全,但花了4个小时来驳斥哲学。。。

原始类型是在使用泛型类型时缺少类型参数。

不应使用原始类型,因为它可能会导致运行时错误,例如将双精度值插入到一组int中。

Set set = new HashSet();
set.add(3.45); //ok

当从集合中检索内容时,你不知道会发生什么。让我们假设您希望它是所有整数,您将其转换为整数;当出现双3.45时,运行时出现异常。

将类型参数添加到Set后,您将立即收到编译错误。这种抢先错误允许您在运行时发生问题之前修复问题(从而节省时间和精力)。

Set<Integer> set = new HashSet<Integer>();
set.add(3.45); //NOT ok.

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

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

您应该指定类型参数。

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

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