问题:

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

其他回答

避免使用原始类型。

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

例如:

列表是原始类型,而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

教程页面。

原始类型是没有任何类型参数的泛型类或接口的名称。例如,给定泛型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>。这允许在编译时检查许多类型安全操作。

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

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

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

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

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

Box rawBox = new Box();

因此,Box是泛型类型Box<T>的原始类型。但是,非泛型类或接口类型不是原始类型。

原始类型出现在遗留代码中,因为许多API类(如Collections类)在JDK5.0之前不是通用的。当使用原始类型时,基本上会得到预泛型行为——一个Box会给你对象。为了向后兼容,允许将参数化类型分配给其原始类型:

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:未选中以获取详细信息。

当使用对原始类型进行操作的旧API时,可能会发生这种情况,如下例所示:

public class WarningDemo {
    public static void main(String[] args){
        Box<Integer> bi;
        bi = createBox();
    }

    static Box createBox(){
        return new Box();
    }
}

术语“未检查”意味着编译器没有足够的类型信息来执行确保类型安全所需的所有类型检查。默认情况下,“未检查”警告被禁用,尽管编译器会给出提示。要查看所有“未检查”警告,请使用-Xlint:unchecked重新编译。

使用-Xlint:unchecked重新编译上一个示例将显示以下附加信息:

WarningDemo.java:4: warning: [unchecked] unchecked conversion
found   : Box
required: Box<java.lang.Integer>
        bi = createBox();
                      ^
1 warning

要完全禁用未检查的警告,请使用-Xlint:未检查标志。@SuppressWarnings(“unchecked”)注释可抑制未选中的警告。如果您不熟悉@SuppressWarnings语法,请参阅注释。

原始来源:Java教程

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

不应使用原始类型,因为它可能会导致运行时错误,例如将双精度值插入到一组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.