栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

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

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

什么是原始类型?

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

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

通过采用通用类型声明的名称而没有随附的类型参数列表而形成的引用类型。

数组类型,其元素类型为原始类型。

未从的超类或超接口继承static的原始类型的非成员类型。RR

这是一个示例说明:

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!)    }}

MyType<E>
是参数化类型(JLS4.5)。通常,通俗地简称MyType为这种类型,但从技术上讲,名称是
MyType<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

现在,我们在运行时遇到了麻烦,因为names其中包含的不是instanceof String。

据推测,如果你想

names
仅仅是遏制
String
,您可以或许仍然使用原始型和手动检查每一个
add
自己,然后手动施放到String每一个项目从names。更好的是,尽管不要使用原始类型,而让编译器利用Java泛型的强大功能为您完成所有工作。

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

当然,如果你不要想names允许Boolean,那么你可以将它声明为List names,和上面的代码将编译。

也可以看看Java教程/泛型

原始类型与

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

以下是来自有效Java 2nd Edition,项目23的引用:不要在新代码中使用原始类型:

原始类型

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

为了说明这一点,请考虑以下方法,该方法采用

a List<Object>
并附加
a new Object()

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

Java中的泛型是不变的。

A List<String>
不是
a List<Object>
,因此以下将生成编译器警告:

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

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

原始类型与

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

List<Object>
List<String>
等等都是
List<?>
,所以说它们只是List替代品可能会很诱人。但是,有一个主要区别:由于
List<E>
仅定义
add(E)
,因此您不能仅将任意对象添加到
List<?>
。另一方面,由于原始类型List没有类型安全性,所以您几乎可以
add
对a进行任何操作List。

请考虑以下片段的以下变体:

static void appendNewObject(List<?> list) {    list.add(new Object()); // compilation error!}//...List<String> names = new ArrayList<String>();appendNewObject(names); // this part is fine!

编译器做了出色的工作,保护您避免潜在地违反

List<?>
!的类型不变性。如果已将参数声明为原始类型
List list
,则代码将编译,并且违反的类型不变式
List<String>
names。

原始类型是该类型的擦除

返回JLS 4.8:

这是可能作为一种类型使用擦除参数化类型或数组类型,其元素类型是参数化类型的擦除。这样的类型称为原始类型。

[…]

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

构造函数,实例方法的

static
类型
C
或未从其超类或超接口继承的原始类型的非字段的类型,是对应于的通用声明中对应于其类型的擦除的原始类型C。

简单来说,当使用原始类型时,构造函数,实例方法和非

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
MyType
getNames
也会被擦除,因此它返回raw
List

JLS 4.6继续解释以下内容:

类型擦除还将映射构造函数或方法的签名到没有参数化类型或类型变量的签名。删除构造函数或方法签名s是一种签名,该签名s与中所给定的所有正式参数类型的名称相同,并且也删除了其中提供的所有形式参数类型s。

如果擦除方法或构造函数的签名,则方法的返回类型以及通用方法或构造函数的类型参数也会被擦除。

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

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

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

这是JLS 4.8的另一句话:

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

有效的Java 2nd Edition也要添加以下内容:

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

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

总而言之,绝对不要在新代码中使用原始类型。您应该始终使用参数化类型。

有没有例外?
不幸的是,由于Java泛型是非泛型的,因此在新代码中必须使用原始类型有两个例外:

  1. 类文字,例如
    List.class,notList<String>.class
  2. instanceof
    操作数,例如
    o instanceof Set
    ,不是
    o instanceof Set<String>


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/437648.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号