首页 > Java >  Java 什么是原始类型,为什么我们不应该使用它?

 Java 什么是原始类型,为什么我们不应该使用它?

上一篇 下一篇

问题:

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

分割线

网友回答:

什么是原始类型?

Java 语言规范定义原始类型如下:

JLS 4.8 原始类型

原始类型定义为以下类型之一:

  • 通过采用不附带类型参数列表的泛型类型声明的名称而形成的引用类型。
  • 元素类型为原始类型的数组类型。
  • 原始类型的非成员类型,它不是从 的超类或超接口继承的。staticRR

下面是一个示例来说明:

public class MyType<E> {
    class Inner { }
    static class Nested { }
    
    public static void main(String[] args) {
        MyType mt;          // warning: MyType is a raw type
        MyType.Inner inn;   // warning: MyType.Inner is a raw type

        MyType.Nested nest; // no warning: not parameterized type
        MyType<Object> mt1; // no warning: type parameter given
        MyType<?> mt2;      // no warning: type parameter given (wildcard OK!)
    }
}

此处是一个参数化类型 (JLS 4.5)。通常通俗地简称此类型,但从技术上讲,名称是 .MyType<E>MyTypeMyType<E>

mt在上述定义中的第一个项目符号点具有原始类型(并生成编译警告); 在第三个项目符号点上也有一个原始类型。inn

MyType.Nested不是参数化类型,即使它是参数化类型的成员类型,因为它是 。MyType<E>static

mt1,并且都是使用实际类型参数声明的,因此它们不是原始类型。mt2


原始类型有什么特别之处?

从本质上讲,原始类型的行为就像引入泛型之前一样。也就是说,以下内容在编译时是完全合法的。

List names = new ArrayList(); // warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // not a compilation error!

上面的代码运行良好,但假设您还具有以下内容:

for (Object o : names) {
    String name = (String) o;
    System.out.println(name);
} // throws ClassCastException!
  //    java.lang.Boolean cannot be cast to java.lang.String

现在我们在运行时遇到了麻烦,因为包含的东西不是 .namesinstanceof String

大概,如果您只想包含 ,您可能仍然可以使用 raw 类型并手动检查每个项目,然后手动转换为 中的每个项目。更好的是,不要使用原始类型,让编译器为您完成所有工作,利用Java泛型的强大功能。namesStringaddStringnames

List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!

当然,如果你确实想允许一个,那么你可以将其声明为 ,上面的代码将编译。namesBooleanList<Object> names

参见

  • Java 教程/泛型

原始类型与用作类型参数有何不同?<Object>

以下是 Effective Java 第 2 版第 23 项的引文: 不要在新代码中使用原始类型:

原始类型和参数化类型之间有什么区别?松散地说,前者选择了泛型类型检查,而后者明确告诉编译器它能够保存任何类型的对象。虽然可以将 传递给类型 的参数,但不能将其传递给 类型的参数。泛型有子类型规则,并且是原始类型的子类型,但不是参数化类型的子类型。因此,如果使用原始类型( List),则会失去类型安全性,但如果使用参数化类型(如 List<Object>),则不会丢失类型安全性ListList<Object>List<String>ListList<Object>List<String>ListList<Object>

为了说明这一点,请考虑以下方法,该方法采用 a 并附加 .List<Object>new Object()

void appendNewObject(List<Object> list) {
   list.add(new Object());
}

Java 中的泛型是不变的。A 不是 ,因此以下内容将生成编译器警告:List<String>List<Object>

List<String> names = new ArrayList<String>();
appendNewObject(names); // compilation error!

如果您声明将原始类型作为参数,那么这将进行编译,因此您将失去从泛型中获得的类型安全性。appendNewObjectList

参见

  • 和 有什么区别?<E extends Number><Number>
  • Java 泛型(非)协方差

原始类型与用作类型参数有何不同?<?>

List<Object>、 等都是,所以可能很容易说它们只是代替。但是,有一个主要区别:由于 仅定义 ,因此不能向 .另一方面,由于原始类型没有类型安全性,因此您几乎可以对 .List<String>List<?>ListList<E>add(E)List<?>ListaddList

请考虑上一个代码段的以下变体:

static void appendNewObject(List<?> list) {
    list.add(new Object()); // compilation error!
}
//...

List<String> names = new ArrayList<String>();
appendNewObject(names); // this part is fine!

编译器在保护您免受可能违反 !如果已将参数声明为 raw 类型,则代码将编译,并且您将违反 的类型不变量。List<?>List listList<String> names


原始类型是该类型的擦除

回到JLS 4.8:

可以将参数化类型的擦除或元素类型为参数化的数组类型的擦除用作类型。此类类型称为原始类型

[…]

原始类型的超类(分别称为超接口)是对泛型类型的任何参数化的超类(超接口)的擦除。

未从其超类或超接口继承的原始类型的构造函数、实例方法或非字段的类型是对应于 的泛型声明中擦除其类型的原始类型。staticCC

简单来说,当使用原始类型时,构造函数、实例方法和非字段也会被删除static

举个例子:

class MyType<E> {
    List<String> getNames() {
        return Arrays.asList("John", "Mary");
    }

    public static void main(String[] args) {
        MyType rawType = new MyType();
        // unchecked warning!
        // required: List<String> found: List
        List<String> names = rawType.getNames();
        // compilation error!
        // incompatible types: Object cannot be converted to String
        for (String str : rawType.getNames())
            System.out.print(str);
    }
}

当我们使用 raw 时,也会被删除,因此它返回一个 raw !MyTypegetNamesList

JLS 4.6 继续解释以下内容:

类型擦除还将构造函数或方法的签名映射到没有参数化类型或类型变量的签名。构造函数或方法签名的擦除是由 中给出的所有形式参数类型同名和擦除组成的签名。sss

如果擦除了方法或构造函数的签名,则还会擦除方法的返回类型和泛型方法或构造函数的类型参数。

泛型方法的签名擦除没有类型参数。

以下错误报告包含编译器开发人员Maurizio Cimadamore和JLS作者之一Alex Buckley关于为什么会发生这种行为的一些想法:https://bugs.openjdk.java.net/browse/JDK-6400189。(简而言之,它使规范更简单。


如果不安全,为什么允许使用原始类型?

这是JLS 4.8中的另一句话:

仅允许使用原始类型作为对旧代码兼容性的让步。强烈建议不要在将泛型引入 Java 编程语言后编写的代码中使用原始类型。Java 编程语言的未来版本可能会禁止使用原始类型。

Effective Java 2nd Edition还增加了以下内容:

既然你不应该使用原始类型,为什么语言设计者允许它们?提供兼容性。

当泛型被引入时,Java平台即将进入第二个十年,并且存在大量不使用泛型的Java代码。所有这些代码保持合法并且可与使用泛型的新代码互操作被认为至关重要。将参数化类型的实例传递给设计用于普通类型的方法必须是合法的,反之亦然。此要求(称为迁移兼容性)推动了支持原始类型的决定。

总之,原始类型永远不应该在新代码中使用。应始终使用参数化类型


没有例外吗?

遗憾的是,由于 Java 泛型是非统一的,因此在新代码中必须使用原始类型有两个例外:

  • 类文字,例如 , notList.classList<String>.class
  • instanceof操作数,例如 , 不是o instanceof Seto instanceof Set<String>

参见

  • 为什么非法?Collection<String>.class

分割线

网友回答:

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

原始类型是Java语言的古老历史。一开始有,他们只持有更多,也不少。对所需铸件的每个操作都从所需的类型开始。CollectionsObjectsCollectionsObject

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

请注意,不可能使用原始类型实现接口。
为什么你不应该使用它们:
CompareAblecompareTo(MyCompareAble)

  • 存储在 中的任何内容都必须强制转换才能使用。ObjectCollection
  • 使用泛型启用编译时检查
  • 使用原始类型与将每个值存储为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);

这与直接使用原始类型时编写的代码相同。以为我不确定接口会发生什么,我想它创建了两个函数,一个采用 a,另一个采用 a 并在转换后将其传递给第一个。CompareAblecompareToMyCompareAbleObject

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

分割线

网友回答:

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

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

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

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

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

Box rawBox = new Box();

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

原始类型显示在旧代码中,因为许多 API 类(如集合类)在 JDK 5.0 之前不是泛型的。当使用原始类型时,你基本上会得到预泛型行为 – a 给你 s。为了向后兼容,允许将参数化类型分配给其原始类型:BoxObject

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 编译器如何使用原始类型的更多信息。

未选中的错误消息

如前所述,将旧代码与泛型代码混合时,可能会遇到类似于以下内容的警告消息:

注意:示例.java使用未经检查或不安全的操作。

注意:使用 -Xlint:uncheck 重新编译以了解详细信息。

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

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

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

术语“未检查”表示编译器没有足够的类型信息来执行确保类型安全所需的所有类型检查。默认情况下,“未选中”警告处于禁用状态,但编译器会给出提示。要查看所有“未选中”警告,请使用 -Xlint:unchecked 重新编译。

使用 -Xlint:uncheck 重新编译前面的示例会显示以下附加信息:

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

要完全禁用未经检查的警告,请使用 -Xlint:-uncheck 标志。批注禁止显示未经检查的警告。如果您不熟悉语法,请参阅批注。@SuppressWarnings("unchecked")@SuppressWarnings

原始来源:Java 教程

模板简介:该模板名称为【 Java 什么是原始类型,为什么我们不应该使用它?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Java】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 252次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 04-20
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
响应式 微信模板 微信图片 微信公众平台 企业网站 微信文章 自适应 微信素材 html5 单页式简历模板
您可能会喜欢的其他模板