40

在计算中,具体化已经意味着一种类型的显式表示——即运行时类型信息。

甲骨文教程说,

可具体化类型是其类型信息在运行时完全可用的类型。这包括原语、非泛型类型、原始类型和未绑定通配符的调用。

不可具体化类型是在编译时通过类型擦除删除信息的类型——调用未定义为无界通配符的泛型类型。

一个类型是可具体化的,如果它是以下之一:

  1. 原始类型(例如int)//理解
  2. 非参数化的类或接口类型(例如NumberStringRunnable) // 为什么
  3. 一种参数化类型,其中所有类型参数都是无界通配符(例如List<?>ArrayList<?>Map<?, ?>) // 为什么
  4. 原始类型(例如ListArrayListMap) // 为什么
  5. 一个数组,其组件类型是可具体化的(例如int[], Number[], List<?>[], List[], 或int[][]) // 为什么

如果类型是以下之一,则它是不可具体化的:

  1. 类型变量(例如T) // 为什么
  2. 带有实际参数的参数化类型(例如List<Number>ArrayList<String>Map<String, Integer>) // 为什么
  3. 带边界的参数化类型(例如List<? extends Number>or Comparable<? super String>) // 为什么

为什么 2,3,4,5 是可物化的,而 6,7,8 是不可物化的?

4

7 回答 7

15

Sun/Oracle 说原因是:

  • 需要:编译时类型检查就足够了
  • 代码大小:避免类似 STL 的代码膨胀
  • 性能:避免在编译时已经完成的运行时类型检查

类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。

简而言之,1-5 是可具体化的,因为它们只是保持与代码中指定的类型相同,因此不会丢失/擦除类型信息,但 6-8 会在编译期间丢失类型信息(<> 之间的东西),所以可以' 不能在运行时访问。

于 2017-03-15T18:31:22.363 回答
11

了解这两个术语的含义。

Reifiable意味着其类型在运行时完全可用意味着 java 编译器不需要任何类型擦除过程。

Non-Reifiable意味着 java 编译器需要类型擦除过程,因为类型不完全可用。

一个类型是可具体化的,如果它是以下之一:

1.原始类型(如int):

这里想,当你写或使用任何一个int作为参考时,你认为编译器需要什么过程来识别int的类型吗?不,因为 int 是 int .... 对于所有原始类型都相同

2. 非参数化的类或接口类型(例如 Number、String 或 Runnable)

与我在上一个答案中所说的相同的答案,编译器不需要对 Number、String 或 Runnable 进行任何类型擦除。

3. 一种参数化类型,其中所有类型参数都是无界通配符(例如 List<?>、ArrayList<?> 或 Map<?, ?>)

所有无界通配符都被接受为可具体化类型,因为它已经在可具体化类型的定义中提及,现在取决于 API 开发人员为什么将其视为可具体化类型。

4. 原始类型(例如 List、ArrayList 或 Map)::

与第一个问题相同的答案

5. 组件类型可具体化的数组(如int[], Number[], List<?>[], List[], or int[][])::

与第一个问题相同的答案

如果类型是以下之一,则它是不可具体化的:

6. 类型变量(如 T):

因为 java 不能识别 T 的类型,所以 Compiler 需要类型擦除来识别类型。

7. 带有实际参数的参数化类型(例如 List<Number>、ArrayList<String> 或 Map<String, Integer>)

这里所有类型都是泛型类型,在运行时编译器将 List 视为 List ...因此根据 Non-refiable 的定义,所有这些集合都被视为不可具体化。

8. 有界的参数化类型(如List<? extends Number> 或Comparable<? super String>)。

与上一个答案相同

于 2015-11-19T20:23:25.960 回答
7

你可以问谷歌同样的问题:

可具体化类型

当您使用泛型时,很多时候,编译时类型信息会丢失。在运行时,程序通常只知道引用是对某种对象的引用。如果所有类型信息在运行时也是已知的,则该类型称为可具体化。也许有一天泛型会被重新设计,以便所有类型都是可具体化的。

于 2013-09-17T11:44:15.527 回答
7

可具体化类型是其类型信息在运行时完全可用的类型。这包括原语、非泛型类型、原始类型和未绑定通配符的调用。

不可具体化类型是在编译时通过类型擦除删除信息的类型——调用未定义为无界通配符的泛型类型。一个不可具体化的类型在运行时没有它的所有信息可用。不可具体化类型的示例是 List<String> 和 List<Number>;JVM 在运行时无法区分这些类型。如对泛型的限制中所示,在某些情况下不能使用不可具体化的类型:例如,在 instanceof 表达式中,或作为数组中的元素。

参考

于 2013-09-17T11:44:49.363 回答
2

Java 最初在 1.1 版本中实现了反射。

5.0 版中引入了泛型类。

在引入泛型时,出于向后兼容性的原因,决定将在运行时擦除泛型类型信息。这允许在泛型之前编写的代码无需修改即可与基于泛型的代码一起操作。

例如 aList[Integer32]会被编译器翻译成Integer32[]. 所有类型检查都将在编译时完成,如果遗漏任何内容,则会产生运行时错误。

这个实现细节意味着泛型没有被具体化(在 VM 中没有具体的实现),因此每当尝试反映泛型类型时,返回的信息将是底层类型的信息。这将是有利的另一个原因是因为无论何时使用虚拟机都不必发出任何具体类型的实现(例如,在 c# 中,每当您使用泛型类型时,它的实际实现都是由虚拟机在运行时生成的,连同反射元数据,因此每当需要生成新类型时都会影响性能)。

于 2017-01-31T12:32:26.970 回答
0

只是一个有根据的猜测,但我怀疑理解这一点的关键是要认识到,虽然 Java 是一种强类型语言,并且在编译过程中验证类型引用,但在许多情况下,实际上并不需要类型信息来执行逻辑。在这种情况下,生成的字节码可能知道它正在使用 Object 的实例,但不知道类型。考虑到不使用强类型的语言可以生成为 java 字节码,这将特别有意义。因此,如果对象类型已被删除,则该实例将是不可具体化的。

于 2016-05-01T22:46:59.887 回答
-1

我不完全确定我理解你的问题,但你可能指的是对象类型而不是原始类型。这个问题更为重要,因为 int 或 double 等原始类型不能用作泛型类型——因此它们的包装类如 Integer。

// This won't work
ArrayList<int> list = new ArrayList<int>();
// But this will
ArrayList<Integer> list = new ArrayList<Integer>();

总而言之,我想说所有对象——而且只有对象——都是可具体化的。(因此可用作泛型类型实例化)

于 2013-09-17T11:46:55.813 回答