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

2024-01-11

问题:

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

什么是原始类型?

Java 语言规范定义了raw type如下:

JLS 4.8 原始类型 https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.8

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

  • 通过采用泛型类型声明的名称而形成的引用类型,而无需附带类型参数列表。

  • 元素类型为原始类型的数组类型。

  • A non-static原始类型的成员类型R不是从超类或超接口继承的R.

下面举一个例子来说明:

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

Here, MyType<E> is a 参数化类型 (JLS 4.5 https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.5)。通常通俗地将此类型简单地称为MyType简而言之,但从技术上讲,名称是MyType<E>.

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

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

mt1, and 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, you could也许仍然使用原始类型并且手动检查每个 add你自己,然后手动施放 to String每件物品来自names. 更好,但不是使用原始类型并且让编译器为你完成所有工作,利用 Java 泛型的力量。

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

当然,如果你DO want names允许一个Boolean,那么你可以将其声明为List<Object> names,上面的代码可以编译。

See also

  • Java 教程/泛型 https://docs.oracle.com/javase/tutorial/java/generics/

raw 类型与 using 有何不同<Object>作为类型参数?

以下是引自《Effective Java》第二版,第 23 条:不要在新代码中使用原始类型:

到底和raw类型有什么区别List和参数化类型List<Object>?宽松地说,前者选择退出泛型类型检查,而后者明确告诉编译器它能够保存任何类型的对象。虽然您可以通过List<String>到一个类型的参数List,你不能将它传递给类型的参数List<Object>。泛型有子类型规则,并且List<String>是原始类型的子类型List,但不是参数化类型List<Object>。作为结果,如果你使用像这样的原始类型,你就会失去类型安全性List,但如果您使用像这样的参数化类型,则不会List<Object>.

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

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

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

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

如果你已经声明appendNewObject采用原始类型List作为参数,那么这将编译,因此您将失去从泛型获得的类型安全性。

See also

  • 有什么区别<E extends Number> and <Number>? https://stackoverflow.com/questions/2770264/what-is-the-difference-between-e-extends-number-and-number/
  • java 泛型(非)协方差 https://stackoverflow.com/questions/2660827/java-generics-covariance

raw 类型与 using 有何不同<?>作为类型参数?

List<Object>, List<String>等都是List<?>,所以可能很容易说它们只是List反而。然而,有一个重大区别:由于List<E>仅定义add(E),您不能将任意对象添加到List<?>。另一方面,由于原始类型List没有类型安全,你可以add几乎任何事情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:

可以用作类型擦除参数化类型的删除或元素类型为参数化类型的数组类型的擦除。这样的类型称为raw type.

[...]

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

构造函数、实例方法或非构造函数的类型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);
    }
}

当我们使用原始MyType, getNames也会被擦除,因此它返回一个原始的List!

JLS 4.6 https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.6继续解释如下:

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

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

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

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


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

这是 JLS 4.8 的另一段引用:

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

有效的Java第二版还有这个要补充:

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

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

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


难道就没有例外吗?

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

  • 类文字,例如List.class, not List<String>.class
  • instanceof操作数,例如o instanceof Set, not o instanceof Set<String>

See also

  • Why is Collection<String>.class非法的? https://stackoverflow.com/questions/2745193/why-is-collectionstring-class-illegal/
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是原始类型以及为什么我们不应该使用它? 的相关文章

  • Java - 如何将特殊字符放入字符串中

    Java 似乎有很好的字符串处理能力 尽管如此 我还是遇到了最简单的问题 我需要动态字符串 它们在运行时更改 因此字符串类型不是一个好的选择 因为它们是不可变的 所以我使用字符数组 设置起来有点痛苦 但至少它们是可以修改的 我想创建一个字符
  • Hashmap并发问题

    我有一个哈希图 出于速度原因 我希望不需要锁定 假设我不介意过时的数据 同时更新它和访问它会导致任何问题吗 我的访问是获取 而不是迭代 删除是更新的一部分 是的 这会导致重大问题 一个例子是向散列映射添加值时可能发生的情况 这可能会导致表重
  • 将 Hibernate 对象序列化为 JSON 时抛出异常

    好吧 我正在使用 Hibernate 将一个小型数据库加载到一些表示表的类并与数据库交互 一切都很好 我真的可以看到所有结果 而且我没有任何空字段 所有这些都已被使用 这里我展示了 主 类 表 import javax persistenc
  • 无法在类对象的 ArrayList 中存储值。 (代码已编辑)

    这基本上是一个 Java 代码转换器 它涉及一个 GUI 让用户输入类类型 名称和方法 为了存储值 我创建了一个类VirtualClass与ArrayList
  • 解决 Java Checkstyle 错误:名称 'logger' 必须匹配模式 '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'

    使用 Eclipse Checkstyle 插件我看到以下错误 名称 logger 必须匹配模式 A Z A Z0 9 A Z0 9 我通过更改解决了此错误 private static final Logger logger Logger
  • 尝试使用 JRI 将 R 与我的 Java 应用程序集成,但出现错误。谁能解释一下原因和解决办法吗?

    我需要将 Java 与 R 集成来运行一些数学命令并使用 R 的功能进行绘图 以下部分代码给出了错误 public static void main String args HelloRWorld r new HelloRWorld r h
  • 正则表达式获取字符串中的第一个数字和其他字符

    我是正则表达式的新手 想知道如何才能只获取字符串中的第一个数字 例如100 2011 10 20 14 28 55 在这种情况下 我希望它返回100 但该数字也可以更短或更长 我在想类似的事情 0 9 但它单独获取每个数字 100 2001
  • Lazy 实现和 .NET 泛型

    我正在寻找进行延迟初始化的方法并发现Lazy
  • 字符串池可以包含两个具有相同值的字符串吗? [复制]

    这个问题在这里已经有答案了 字符串池可以包含两个具有相同值的字符串吗 String str abc String str1 new String abc Will the second statement with new operator
  • 有多少种方法可以将位图转换为字符串,反之亦然?

    在我的应用程序中 我想以字符串的形式将位图图像发送到服务器 我想知道有多少种方法可以将位图转换为字符串 现在我使用 Base64 格式进行编码和解码 它需要更多的内存 是否有其他可能性以不同的方式做同样的事情 从而消耗更少的内存 现在我正在
  • 容器中的 JVM 计算处理器错误?

    最近我又做了一些研究 偶然发现了这一点 在向 OpenJDK 团队抱怨之前 我想看看是否有其他人观察到这一点 或者不同意我的结论 因此 众所周知 JVM 长期以来忽略了应用于 cgroup 的内存限制 众所周知 现在从 Java 8 更新某
  • 线程“main”中的异常 java.lang.StackOverflowError

    我有一段代码 但我无法弄清楚为什么它在线程 main java lang StackOverflowError 中给出异常 这是问题 Given a positive integer n prints out the sum of the
  • 如何找到被点击的JLabel并从中显示ImageIcon?

    这是我的代码 我想知道哪个l单击 然后在新框架中显示该 ImageIcon e getSource 不起作用 final JFrame shirts new JFrame T shirts JPanel panel new JPanel n
  • 改变for循环的顺序?

    我遇到一种情况 我需要根据用户输入以不同的顺序循环遍历 xyz 坐标 所以我是 3D 空间中的一个区域 然后是一组像这样的 for 循环 for int x 0 x lt build getWidth x for int y 0 y lt
  • 如何在Java媒体框架中学习.wav持续时间?

    我正在尝试使用 java 媒体框架将 mov 文件与 wav 文件合并 因此我需要知道它们的持续时间 我怎样才能做到这一点 任何想法 将不胜感激 您可以使用以下方式了解声音文件的持续时间 即 VitalyVal 的第二种方式 import
  • Hibernate HQL:将对值作为 IN 子句中的参数传递

    我面临一个问题 如何使用 IN 子句将查询中的成对值的参数传递给 HQL 例如 select id name from ABC where id reg date in x y 并且参数是不同的数据类型string id 和reg date
  • JavaFX - 为什么多次将节点添加到窗格或不同的窗格会导致错误?

    我现在正在学习基本的 JavaFX 我不明白我正在阅读的书中的这一说法 不 诸如文本字段之类的节点只能添加到一个窗格中一次 将节点添加到多次窗格或不同的窗格将导致运行时错误 我可以从书中提供的UML图看出它是一个组合 但我不明白为什么 库类
  • 了解 Spark 中的 DAG

    问题是我有以下 DAG 我认为当需要洗牌时 火花将工作划分为不同的阶段 考虑阶段 0 和阶段 1 有些操作不需要洗牌 那么为什么 Spark 将它们分成不同的阶段呢 我认为跨分区的实际数据移动应该发生在第 2 阶段 因为这里我们需要cogr
  • 如何解决 PDFBox 没有 unicode 映射错误?

    我有一个现有的 PDF 文件 我想使用 python 脚本将其转换为 Excel 文件 目前正在使用PDFBox 但是存在多个类似以下错误 org apache pdfbox pdmodel font PDType0Font toUnico
  • 为什么这个私人浮动字段变为零?

    我有一些奇怪的行为 我很难向自己解释 称为 textureScale 的浮点字段变为零 如果某些代码正在更改该值 则可以解释这一点 然而 我希望能够通过将其设置为 私有最终浮点 来导致构建失败 或者至少是运行时异常 那么无论更改该值都将失败

随机推荐