Java 的类型擦除有什么好处?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20918650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:19:11  来源:igfitidea点击:

What are the benefits of Java's types erasure?

javatype-erasure

提问by vertti

I read a tweettoday that said:

我今天读到一条推文说:

It's funny when Java users complain about type erasure, which is the only thing Java got right, while ignoring all the things it got wrong.

当 Java 用户抱怨类型擦除时很有趣,这是 Java 唯一正确的事情,而忽略了所有错误的事情。

Thus my question is:

因此我的问题是:

Are there benefits from Java's type erasure? What are the technical or programming style benefits it (possibly) offers other than the JVM implementations preference for backwards compatibility and runtime performance?

Java 的类型擦除有什么好处吗?除了 JVM 实现对向后兼容性和运行时性能的偏好之外,它(可能)提供哪些技术或编程风格的好处?

采纳答案by Sukant Hajra

Type Erasure Is Good

类型擦除是好的

Let's stick to the facts

让我们坚持事实

A lot of the answers thus far are overly concerned with the Twitter user. It's helpful to keep focused on the messages and not the messenger. There is a fairly consistent message with even just the excerpts mentioned thus far:

到目前为止,很多答案都过度关注 Twitter 用户。专注于消息而不是信使是有帮助的。即使只是到目前为止提到的摘录,也有一个相当一致的信息:

It's funny when Java users complain about type erasure, which is the only thing Java got right, while ignoring all the things it got wrong.

I get huge benefits (e.g. parametricity) and nil cost (alleged cost is a limit of imagination).

new T is a broken program. It is isomorphic to the claim "all propositions are true." I am not big into this.

当 Java 用户抱怨类型擦除时很有趣,这是 Java 唯一正确的事情,而忽略了所有错误的事情。

我得到了巨大的好处(例如参数化)和零成本(所谓的成本是想象力的极限)。

new T 是一个损坏的程序。它同构于“所有命题都是真的”这一主张。我对此并不感兴趣。

A goal: reasonable programs

一个目标:合理的方案

These tweets reflect a perspective that is not interested in whether we can make the machine do something, but more whether we can reason that the machine will do something we actually want. Good reasoning is a proof. Proofs can be specified in formal notation or something less formal. Regardless of the specification language, they must be clear and rigorous. Informal specifications are not impossible to structure correctly, but are often flawed in practical programming. We end up with remediations like automated and exploratory tests to make up for the problems we have with informal reasoning. This is not to say that testing is intrinsically a bad idea, but the quoted Twitter user is suggesting that there is a much better way.

这些推文反映的观点不是我们是否可以让机器做某事,而是我们是否可以推理机器会做我们真正想做的事情。好的推理是一个证明。证明可以用正式的符号或不太正式的东西来指定。无论使用何种规范语言,它们都必须清晰、严谨。非正式规范并非不可能正确构建,但在实际编程中往往存在缺陷。我们最终通过自动化和探索性测试等补救措施来弥补我们在非正式推理方面遇到的问题。这并不是说测试本质上是一个坏主意,但引用的 Twitter 用户表示有更好的方法。

So our goal is to have correct programs that we can reason about clearly and rigorously in a way that corresponds with how the machine will actually execute the program. This, though, is not the only goal. We also want our logic to have a degree of expressivity. For example, there's only so much we can express with propositional logic. It's nice to have universal (?) and existential (?) quantification from something like first-order logic.

因此,我们的目标是拥有正确的程序,我们可以以与机器实际执行程序的方式相对应的方式清晰而严格地推理这些程序。然而,这并不是唯一的目标。我们还希望我们的逻辑具有一定程度的表现力。例如,我们只能用命题逻辑表达这么多。从一阶逻辑之类的东西中获得普遍(?)和存在(?)量化是很好的。

Using type systems for reasoning

使用类型系统进行推理

These goals can be very nicely addressed by type systems. This is especially clear because of the Curry-Howard correspondence. This correspondence is often expressed with the following analogy: types are to programs as theorems are to proofs.

类型系统可以很好地解决这些目标。由于库里-霍华德的对应关系,这一点尤其明显。这种对应关系通常用以下类比来表示:类型之于程序,正如定理之于证明。

This correspondence is somewhat profound. We can take logical expressions, and translate them through the correspondence to types. Then if we have a program with the same type signature that compiles, we have proven that the logical expression is universally true (a tautology). This is because the correspondence is two-way. The transformation between the type/program and the theorem/proof worlds are mechanical, and can in many cases be automated.

这个对应有点深刻。我们可以把逻辑表达式,通过类型的对应关系来翻译。然后,如果我们有一个具有相同类型签名的程序可以编译,我们就证明了逻辑表达式是普遍正确的(重言式)。这是因为对应是双向的。类型/程序和定理/证明世界之间的转换是机械的,并且在许多情况下可以自动化。

Curry-Howard plays nicely into what we'd like to do with specifications for a program.

库里-霍华德(Curry-Howard)很好地满足了我们对程序规范的要求。

Are type systems useful in Java?

类型系统在 Java 中有用吗?

Even with an understanding of Curry-Howard, some people find it easy to dismiss the value of a type system, when it

即使了解 Curry-Howard,有些人也发现很容易忽视类型系统的价值,当它

  1. is extremely hard to work with
  2. corresponds (through Curry-Howard) to a logic with limited expressivity
  3. is broken (which gets to the characterization of systems as "weak" or "strong").
  1. 非常难以合作
  2. 对应(通过 Curry-Howard)一个表达能力有限的逻辑
  3. 被破坏(这可以将系统描述为“弱”或“强”)。

Regarding the first point, perhaps IDEs make Java's type system easy enough to work with (that's highly subjective).

关于第一点,也许 IDE 使 Java 的类型系统易于使用(这是非常主观的)。

Regarding the second point, Java happens to almostcorrespond to a first-order logic. Generics give use the type system equivalent of universal quantification. Unfortunately, wildcards only give us a small fraction of existential quantification. But universal quantification is pretty good start. It's nice to be able to say that functions for List<A>work universally for all possiblelists because A is completely unconstrained. This leads to what the Twitter user is talking about with respect to "parametricity."

关于第二点,爪哇恰好几乎对应于第一阶逻辑。泛型使用等同于通用量化的类型系统。不幸的是,通配符只给了我们存在量化的一小部分。但普遍量化是一个很好的开始。很高兴能够说函数List<A>所有可能的列表通用,因为 A 是完全不受约束的。这导致了 Twitter 用户所谈论的“参数化”。

An often-cited paper about parametricity is Philip Wadler's Theorems for free!. What's interesting about this paper is that from just the type signature alone, we can prove some very interesting invariants. If we were to write automated tests for these invariants we would be very much wasting our time. For example, for List<A>, from the type signature alone for flatten

一篇经常被引用的关于参数化的论文是 Philip Wadler 的免费定理!. 这篇论文的有趣之处在于,仅从类型签名,我们就可以证明一些非常有趣的不变量。如果我们要为这些不变量编写自动化测试,我们将非常浪费时间。例如, for List<A>,仅从类型签名 forflatten

<A> List<A> flatten(List<List<A>> nestedLists);

we can reason that

我们可以推断

flatten(nestedList.map(l -> l.map(any_function)))
    ≡ flatten(nestList).map(any_function)

That's a simple example, and you can probably reason about it informally, but it's even nicer when we get such proofs formally for free from the type system and checked by the compiler.

这是一个简单的例子,你可能可以非正式地推理它,但是当我们从类型系统免费获得这样的证明并由编译器检查时,它会更好。

Not erasing can lead to abuses

不擦除会导致滥用

From the perspective of language implementation, Java's generics (which correspond to universal types) play very heavily into the parametricity used to get proofs about what our programs do. This gets to the third problem mentioned. All these gains of proof and correctness require a sound type system implemented without defects. Java definitely has some language features that allow us to shatter our reasoning. These include but are not limited to:

从语言实现的角度来看,Java 的泛型(对应于通用类型)在参数化中扮演着非常重要的角色,用于获得我们的程序所做的证明。这就涉及到第三个问题。所有这些证明和正确性的收益都需要一个健全的类型系统,没有缺陷。Java 肯定有一些语言特性可以让我们打破我们的推理。这些包括但不限于:

  • side-effects with an external system
  • reflection
  • 外部系统的副作用
  • 反射

Non-erased generics are in many ways related to reflection. Without erasure there's runtime information that's carried with the implementation that we can use to design our algorithms. What this means is that statically, when we reason about programs, we don't have the full picture. Reflection severely threatens the correctness of any proofs we reason about statically. It's no coincidence reflection also leads to a variety of tricky defects.

非擦除泛型在很多方面都与反射有关。在没有擦除的情况下,我们可以用来设计算法的实现会携带运行时信息。这意味着静态地,当我们对程序进行推理时,我们并没有全面了解。反射严重威胁我们静态推理的任何证明的正确性。并非巧合,反射也会导致各种棘手的缺陷。

So what are ways that non-erased generics might be "useful?" Let's consider the usage mentioned in the tweet:

那么,非擦除泛型可能“有用”的方式有哪些?让我们考虑一下推文中提到的用法:

<T> T broken { return new T(); }

What happens if T doesn't have a no-arg constructor? In some languages what you get is null. Or perhaps you skip the null value and go straight to raising an exception (which null values seem to lead to anyway). Because our language is Turing complete, it's impossible to reason about which calls to brokenwill involve "safe" types with no-arg constructors and which ones won't. We've lost the certainty that our program works universally.

如果 T 没有无参数构造函数会发生什么?在某些语言中,您得到的是空值。或者您可能跳过空值并直接引发异常(空值似乎无论如何都会导致)。因为我们的语言是图灵完备的,所以无法推断哪些调用broken会涉及带有无参数构造函数的“安全”类型,哪些不会。我们已经失去了我们的程序普遍适用的确定性。

Erasing means we've reasoned (so let's erase)

擦除意味着我们已经推理(所以让我们擦除)

So if we want to reason about our programs, we're strongly advised to not employ language features that strongly threaten our reasoning. Once we do that, then why not just drop the types at runtime? They're not needed. We can get some efficiency and simplicity with the satisfaction that no casts will fail or that methods might be missing upon invocation.

因此,如果我们想对我们的程序进行推理,我们强烈建议不要使用严重威胁我们推理的语言特性。一旦我们这样做了,那为什么不直接在运行时删除类型呢?他们不需要。我们可以获得一些效率和简单性,满意的是没有强制转换会失败或者调用时可能会丢失方法。

Erasing encourages reasoning.

擦除鼓励推理。

回答by Evgeniy Dorofeev

One good thing is that there was no need to change JVM when generics were introduced. Java implements generics at compiler level only.

一件好事是在引入泛型时无需更改 JVM。Java 仅在编译器级别实现泛型。

回答by necromancer

avoids c++-like code bloat because the same code is used for multiple types; however, type erasure requires virtual dispatch whereas the c++-code-bloat approach can do non-virtually dispatched generics

避免类似 c++ 的代码膨胀,因为相同的代码用于多种类型;然而,类型擦除需要虚拟分派,而 c++-code-bloat 方法可以执行非虚拟分派的泛型

回答by ruakh

A subsequent post by the same user in the same conversation:

同一用户在同一对话中的后续帖子:

new T is a broken program. It is isomorphic to the claim "all propositions are true." I am not big into this.

new T 是一个损坏的程序。它同构于“所有命题都是真的”这一主张。我对此并不感兴趣。

(This was in response to a statement by another user, namely that "it seems in some situations 'new T' would be better", the idea being that new T()is impossible due to type erasure. (This is debatable — even if Twere available at runtime, it could be an abstract class or interface, or it could be Void, or it could lack a no-arg constructor, or its no-arg constructor could be private (e.g., because it's supposed to be a singleton class), or its no-arg constructor could specify a checked exception that the generic method does not catch or specify — but that was the premise. Regardless, it's true that without erasure you could at least write T.class.newInstance(), which handles those issues.))

(这是对另一位用户的声明的回应,即“在某些情况下,'new T' 似乎会更好”,这个想法new T()是不可能的,因为类型擦除。(这是有争议的——即使T在运行时,它可能是一个抽象类或接口,或者它可能是Void,或者它可能缺少一个无参数构造函数,或者它的无参数构造函数可能是私有的(例如,因为它应该是一个单例类),或者它的no-arg 构造函数可以指定泛型方法未捕获或指定的已检查异常 - 但这是前提。无论如何,确实,无需擦除,您至少可以编写T.class.newInstance()处理这些问题的 )。

This view, that types are isomorphic to propositions, suggests that the user has a background in formal type theory. (S)he very likely does not like "dynamic types" or "runtime-types" and would prefer a Java without downcasts and instanceofand reflection and so on. (Think of a language like Standard ML, which has a very rich (static) type system and whose dynamic semantics do not depend on any type information whatsoever.)

这种认为类型与命题同构的观点表明用户具有形式类型理论的背景。(S) 他很可能不喜欢“动态类型”或“运行时类型”,并且更喜欢没有向下转换instanceof和反射等的 Java 。(想想像 Standard ML 这样的语言,它有一个非常丰富的(静态)类型系统,其动态语义不依赖于任何类型信息。)

It's worth keeping in mind, by the way, that the user is trolling: while (s)he likely sincerely prefers (statically) typed languages, (s)he is notsincerely trying to persuade others of that view. Rather, the main purpose of the original tweet was to mock those who disagree, and after some of those disagree-ers chimed in, the user posted follow-up tweets such as "the reason java has type erasure is that Wadler et al know what they are doing, unlike users of java". Unfortunately, this makes it hard to find out what (s)he's actually thinking; but fortunately, it also likely means that it's not very important to do so. People with actual depth to their views don't generally resort to trolls that are quitethis content-free.

顺便说一下,值得记住的是,用户是在欺骗:虽然他可能真诚地喜欢(静态)类型语言,但他并没有真诚地试图说服其他人接受这种观点。相反,原始推文的主要目的是嘲笑那些不同意的人,在一些不同意的人插话之后,用户发布了后续推文,例如“java 具有类型擦除的原因是 Wadler 等人知道什么他们正在做,不像java用户”。不幸的是,这让我们很难知道他到底在想什么;但幸运的是,这也可能意味着这样做不是很重要。实际深入到他们的观点的人通常不会诉诸于那些巨魔相当此内容免费。

回答by techtangents

Types are a construct used for writing programs in a manner that allows the compiler to check the correctness of a program. A type is a proposition on a value - the compiler verifies that this proposition is true.

类型是用于以允许编译器检查程序正确性的方式编写程序的构造。类型是对值的一个命题——编译器验证这个命题是否为真。

During the execution of a program, there should be no need for type information - this has already been verified by the compiler. The compiler should be free to discard this information in order to perform optimisations on the code - make it run faster, generate a smaller binary etc. Erasure of type parameters facilitates this.

在程序执行期间,应该不需要类型信息——这已经被编译器验证过。编译器应该可以自由地丢弃这些信息,以便对代码进行优化——让它运行得更快,生成更小的二进制文件等等。类型参数的擦除有助于实现这一点。

Java breaks static typing by allowing type information to be queried at runtime - reflection, instanceof etc. This allows you to construct programs that cannot be statically verified - they bypass the type system. It also misses opportunities for static optimisation.

Java 通过允许在运行时查询类型信息(反射、instanceof 等)来打破静态类型。这允许您构建无法静态验证的程序——它们绕过类型系统。它也错过了静态优化的机会。

The fact that type parameters are erased prevents some instances of these incorrect programs to be constructed, however, more incorrect programs would be disallowed if more type information was erased and the reflection and instanceof facilities were removed.

类型参数被擦除的事实阻止了这些不正确程序的一些实例被构造,但是,如果更多的类型信息被擦除并且反射和 instanceof 设施被删除,更多的错误程序将被禁止。

Erasure is important for upholding the property of "parametricity" of a data type. Say I have a type "List" parameterised over component type T. i.e. List<T>. That type is a proposition that this List type works identically for any type T. The fact that T is an abstract, unbounded type parameter means that we know nothing about this type, therefore are prevented from doing anything special for special cases of T.

擦除对于维护数据类型的“参数化”属性很重要。假设我有一个通过组件类型 T 参数化的“List”类型。即 List<T>。该类型是一个命题,即此 List 类型对于任何类型 T 都相同。 T 是一个抽象的、无界类型参数的事实意味着我们对该类型一无所知,因此无法对 T 的特殊情况做任何特殊的事情。

e.g. say I have a List xs = asList("3"). I add an element: xs.add("q"). I end up with ["3","q"]. Since this is parametric, I can assume that List xs = asList(7); xs.add(8) ends up with [7,8] I know from the type that it doesn't do one thing for String and one thing for Int.

例如说我有一个列表 xs = asList("3")。我添加了一个元素:xs.add("q")。我最终得到 ["3","q"]。由于这是参数化的,我可以假设 List xs = asList(7); xs.add(8) 以 [7,8] 结束,我从类型中知道它不会对 String 做一件事,对 Int 做一件事。

Furthermore, I know that the List.add function can not invent values of T out of thin air. I know that if my asList("3") has a "7" added to it, the only possible answers would be constructed out of the values "3" and "7". There is no possibility of a "2" or "z" being added to the list because the function would be unable to construct it. Neither of these other values would be sensible to add, and parametricity prevents these incorrect programs from being constructed.

此外,我知道 List.add 函数不能凭空创造 T 的值。我知道如果我的 asList("3") 添加了一个 "7",那么唯一可能的答案将由值 "3" 和 "7" 构成。不可能将“2”或“z”添加到列表中,因为函数将无法构造它。添加这些其他值都不是明智之举,并且参数化可以防止构建这些不正确的程序。

Basically, erasure prevents some means of violating parametricity, thus eliminating possibilities of incorrect programs, which is the goal of static typing.

基本上,擦除可以防止某些违反参数的方法,从而消除错误程序的可能性,这是静态类型的目标。

回答by Lachlan

The reason type erasure is a good thing is that the things it makes impossible are harmful. Preventing the inspection of type arguments at runtime makes understanding and reasoning about programs easier.

类型擦除是一件好事的原因是它使不可能的事情是有害的。防止在运行时检查类型参数可以更容易地理解和推理程序。

An observation that I found somewhat counter-intuitive is that when function signatures are moregeneric, they become easier to understand. This is because the number of possible implementations is reduced. Consider a method with this signature, which we somehow know has no side effects:

我发现有些违反直觉的观察结果是,当函数签名通用时,它们变得更容易理解。这是因为可能实现的数量减少了。考虑一个带有这个签名的方法,我们知道它没有副作用:

public List<Integer> XXX(final List<Integer> l);

What are the possible implementations of this function? Very many. You can tell very little about what this function does. It could be reversing the input list. It could be pairing ints together, summing them and returning a list half the size. There are many other possibilities that could be imagined. Now consider:

这个函数有哪些可能的实现方式?非常多。关于这个函数的作用,你只能说很少。它可能正在反转输入列表。它可以将整数配对在一起,将它们相加并返回一个大小减半的列表。还有很多其他的可能性可以想象。现在考虑:

public <T> List<T> XXX(final List<T> l);

How many implementations of this function are there? Since the implementation cannot know the type of the elements, a huge number of implementations can now be excluded: elements cannot be combined, or added to the list or filtered out, et al. We are limited to things like: identity (no change to the list), dropping elements, or reversing the list. This function is easier to reason about based on its signature alone.

这个函数有多少个实现?由于实现无法知道元素的类型,因此现在可以排除大量的实现:元素无法组合、添加到列表或过滤掉等。我们仅限于:身份(不更改列表)、删除元素或反转列表。此函数仅根据其签名更容易推理。

Except… in Java you can always cheat the type system. Because the implementation of that generic method can use stuff like instanceofchecks and/or casts to arbitrary types, our reasoning based on the type signature can be easily rendered useless. The function couldinspect the type of the elements and do any number of things based on the result. If these runtime hacks are allowed, parameterised method signatures become much less useful to us.

除了……在 Java 中,你总是可以欺骗类型系统。因为该泛型方法的实现可以使用诸如instanceof检查和/或转换为任意类型的东西,所以我们基于类型签名的推理很容易变得无用。该函数可以检查元素的类型并根据结果执行任意数量的操作。如果允许这些运行时黑客,参数化方法签名对我们来说就没那么有用了。

If Java did not have type erasure (that is, type arguments were reified at runtime) then this would simply allow morereasoning-impairing shenanigans of this kind. In the above example, the implementation can only violate the expectations set by the type signature if the list has at least one element; but if Twas reified, it could do so even if the list were empty. Reified types would just increase the (already very many) possibilities for impeding our understanding of the code.

如果 Java 没有类型擦除(即,类型参数在运行时被具体化),那么这只会允许更多此类影响推理的恶作剧。在上面的例子中,如果列表至少有一个元素,则实现只能违反类型签名设置的期望;但是如果T被具体化,即使列表是空的,它也可以这样做。具体化的类型只会增加(已经非常多)阻碍我们理解代码的可能性。

Type erasure makes the language less "powerful". But some forms of "power" are actually harmful.

类型擦除使语言不那么“强大”。但某些形式的“权力”实际上是有害的。

回答by Jules

An additional point none of the other answers seem to have considered: if you really need generics with run-time typing, you can implement it yourselflike this:

其他答案似乎都没有考虑过的另一点:如果您确实需要具有运行时类型的泛型,您可以像这样自己实现它

public class GenericClass<T>
{
     private Class<T> targetClass;
     public GenericClass(Class<T> targetClass)
     {
          this.targetClass = targetClass;
     }

This class is then able to do all the things that would be achievable by default if Java did not use erasure: it can allocate new Ts (assuming Thas a constructor that matches the pattern it expects to use), or arrays of Ts, it can dynamically test at run time if a particular object is a Tand change behaviour depending on that, and so on.

如果 Java 不使用擦除,这个类就能够完成默认情况下可以实现的所有事情:它可以分配 new Ts(假设T有一个与它期望使用的模式匹配的构造函数),或者Ts 的数组,它可以在运行时动态测试特定对象是否为 aT并根据该对象更改行为,依此类推。

For example:

例如:

     public T newT () { 
         try {
             return targetClass.newInstance(); 
         } catch(/* I forget which exceptions can be thrown here */) { ... }
     }

     private T value;
     /** @throws ClassCastException if object is not a T */
     public void setValueFromObject (Object object) {
         value = targetClass.cast(object);
     }
}

回答by Marko Topolnik

The one thing I don't see considered here at all is that OOP's runtime polymorphismis fundamentally dependent on the reification of types at runtime. When a language whose backbone is held in place by refied types introduces a major extension to its type system and bases it on type erasure, cognitive dissonance is the inevitable outcome. This is precisely what happened to the Java community; it is why type erasure has attracted so much controversy, and ultimately why there are plans to undo it in a future release of Java. Finding something funnyin that complaint of Java users betrays either an honest misunderstanding of the spirit of Java, or a consciously deprecating joke.

我在这里根本没有看到的一件事是 OOP 的运行时多态性从根本上依赖于运行时类型的具体化。当一种主干由重构类型固定的语言引入了对其类型系统的主要扩展并将其建立在类型擦除的基础上时,认知失调是不可避免的结果。这正是 Java 社区发生的事情;这就是为什么类型擦除引起了如此多的争议,以及最终为什么有计划在 Java 的未来版本中撤消它。在 Java 用户的抱怨中发现一些有趣的东西,要么是对 Java 精神的诚实误解,要么是有意贬低的笑话。

The claim "erasure is the only thing Java got right" implies the claim that "all languages based on dynamic dispatch against the runtime type of function argument are fundamentally flawed". Although certainly a legitimate claim on its own, and one which can even be considered as valid criticism of all OOP languages including Java, it cannot lodge itself as a pivotal point from which to evaluate and criticise features within the context of Java, where runtime polymorphism is axiomatic.

声称“擦除是 Java 唯一正确的事情”意味着“所有基于动态调度的语言针对运行时类型的函数参数都存在根本缺陷”。虽然它本身当然是一个合法的主张,甚至可以被视为对包括 Java 在内的所有 OOP 语言的有效批评,但它不能将自己作为评估和批评Java 上下文中的特性的关键点,其中运行时多态性是公理的。

In summary, while one may validly state "type erasure is the way to go in language design", positions supporting type erasure within Java are misplaced simply because it is much, much too late for thatand had already been so even at the historical moment when Oak was embraced by Sun and renamed to Java.

总而言之,虽然人们可能会说“类型擦除是语言设计的必经之路”,但在 Java 中支持类型擦除的立场是错位的,因为它太晚了,即使在历史时刻也已经如此当 Oak 被 Sun 接受并更名为 Java 时。





As to whether static typing itself is the proper direction in the design of programming languages, this fits into a much wider philosophical context of what we think constitutes the activity of programming. One school of thought, clearly deriving from the classical tradition of mathematics, sees programs as instances of one mathematical concept or other (propositions, functions, etc.), but there is an entirely different class of approaches, which see programming as a way to talk to the machine and explain what we want from it. In this view the program is a dynamic, organically growing entity, a dramatic opposite of the carefully erected aedifice of a statically typed program.

至于静态类型本身是否是编程语言设计的正确方向,这符合我们认为构成编程活动的更广泛的哲学背景。一种思想流派显然源自数学的经典传统,将程序视为一个或其他数学概念(命题、函数等)的实例,但有一类完全不同的方法,将编程视为一种方法与机器交谈并解释我们想从中得到什么。在这种观点中,程序是一个动态的、有机增长的实体,与静态类型程序精心建造的大厦形成鲜明对比。

It would seem natural to regard the dynamic languages as being a step in that direction: the consistency of the program emerges from the bottom up, with no a prioriconstrants which would impose it in a top-down manner. This paradigm can be seen as a step towards modeling the process whereby we, the humans, become what we are through development and learning.

将动态语言视为朝这个方向迈出的一步似乎很自然:程序的一致性是自下而上出现的,没有先验约束以自上而下的方式强加它。这种范式可以看作是对我们人类通过发展和学习成为我们现在的样子这一过程建模的一步。

回答by Marko Topolnik

(Although I already wrote an answer here, revisiting this question two years later I realize there is another, completely different way of answering it, so I'm leaving the previous answer intact and adding this one.)

(虽然我已经在这里写了一个答案,两年后重新审视这个问题,我意识到还有另一种完全不同的回答方式,所以我保留原先的答案并添加这个。)



It is highly arguable whether the process done on Java Generics deserves the name "type erasure". Since generic types are not erased but replaced with their raw counterparts, a better choice seems to be "type mutilation".

在 Java 泛型上完成的过程是否配得上“类型擦除”这个名字是很有争议的。由于泛型类型没有被删除而是用它们的原始对应物替换,更好的选择似乎是“类型切割”。

The quintessential feature of type erasure in its commonly understood sense is forcing the runtime to stay within the boundaries of the static type system by making it "blind" to the structure of the data it accesses. This gives full power to the compiler and allows it to prove theorems based on static types alone. It also helps the programmer by constraining the code's degrees of freedom, giving more power to simple reasoning.

在通常理解的意义上,类型擦除的典型特征是通过使其对其访问的数据结构“视而不见”,从而迫使运行时停留在静态类型系统的边界内。这为编译器提供了全部功能,并允许它仅基于静态类型来证明定理。它还通过约束代码的自由度来帮助程序员,赋予简单推理更多的权力。

Java's type erasure does not achieve that—it cripples the compiler, like in this example:

Java 的类型擦除没有实现这一点——它削弱了编译器,就像在这个例子中一样:

void doStuff(List<Integer> collection) { 
}

void doStuff(List<String> collection) // ERROR: a method cannot have 
                   // overloads which only differ in type parameters

(The above two declarations collapse into the same method signature after erasure.)

(以上两个声明在擦除后折叠为相同的方法签名。)

On the flip side, the runtime can still inspect the type of an object and reason about it, but since its insight into the true type is crippled by erasure, static type violations are trivial to achieve and hard to prevent.

另一方面,运行时仍然可以检查对象的类型并对其进行推理,但是由于它对真实类型的洞察被擦除削弱了,因此静态类型违规很容易实现并且难以防止。

To make things even more convoluted, the original and erased type signatures co-exist and are considered in parallel during compilation. This is because the whole process is not about removing type information from the runtime, but about shoehorning a generic type system into a legacy raw type system to maintain backwards compatibility. This gem is a classic example:

更复杂的是,原始类型签名和已擦除类型签名共存并在编译期间并行考虑。这是因为整个过程不是从运行时中删除类型信息,而是将泛型类型系统硬塞进遗留原始类型系统以保持向后兼容性。这个 gem 是一个经典的例子:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

(The redundant extends Objecthad to be added to preserve backward compatibility of the erased signature.)

extends Object必须添加冗余以保持已擦除签名的向后兼容性。)

Now, with that in mind, let us revisit the quote:

现在,考虑到这一点,让我们重新审视这句话:

It's funny when Java users complain about type erasure, which is the only thing Java got right

当 Java 用户抱怨类型擦除时很有趣,这是 Java 唯一正确的事情

What exactlydid Java get right? Is it the word itself, regardless of meaning? For contrast take a look at the humble inttype: no runtime type check is ever performed, or even possible, and the execution is always perfectly type-safe. That'swhat type erasure looks like when done right: you don't even know it's there.

什么确切并得到Java的吧?无论其含义如何,它是单词本身吗?相比之下,看看不起眼的int类型:从来没有执行过运行时类型检查,甚至没有执行过,而且执行总是完全类型安全的。就是类型擦除在正确完成时的样子:您甚至不知道它的存在。

回答by usr-local-ΕΨΗΕΛΩΝ

This is not a direct answer (OP asked "what are the benefits", I am replying "what are the cons")

这不是一个直接的答案(OP 问“有什么好处”,我回答的是“有什么缺点”)

Compared to C# type system, Java type erasure is a real pain for two raesons

与 C# 类型系统相比,Java 类型擦除是两个原因的真正痛苦

You can't implement an interface twice

你不能两次实现一个接口

In C# you can implement both IEnumerable<T1>and IEnumerable<T2>safely, especially if the two types do not share a common ancestor (i.e. their ancestor isObject).

在 C# 中,您既可以安全地实现IEnumerable<T1>,也可以IEnumerable<T2>安全地实现,尤其是当这两种类型不共享共同的祖先时(即它们的祖先Object)。

Practical example: in Spring Framework, you can't implement ApplicationListener<? extends ApplicationEvent>multiple times. If you need different behaviours based on Tyou need to test instanceof

实际例子:在Spring Framework中,不能ApplicationListener<? extends ApplicationEvent>多次实现。如果您需要基于T您需要测试的不同行为instanceof

You can't do new T()

你不能做 new T()

(and you need a reference to Class to do that)

(你需要引用 Class 来做到这一点)

As others commented, doing the equivalent of new T()can only be done via reflection, only by invoking an instance of Class<T>, making sure about the parameters required by the constructor. C# allows you to do new T()onlyif you constrain Tto parameterless constructor. If Tdoes not respect that constraint, a compile erroris raised.

正如其他人评论的new T()那样,只能通过反射完成等效的操作,只能通过调用 的实例Class<T>,确保构造函数所需的参数。C# 允许您new T()在限制T为无参数构造函数时执行此操作。如果T不遵守该约束,则会引发编译错误

In Java, you will often be forced to write methods that look like the following

在 Java 中,您经常会被迫编写如下所示的方法

public <T> T create(....params, Class<T> classOfT)
{

    ... whatever you do
    ... you will end up
    T = classOfT.newInstance();


    ... or more advanced reflection
    Constructor<T> parameterizedConstructorThatYouKnowAbout = classOfT.getConstructor(...,...);
}

The drawbacks in the above code are:

上述代码的缺点是:

  • Class.newInstanceonly works with a parameterless constructor. If none available, ReflectiveOperationExceptionis thrown at runtime
  • Reflected constructor does not highlight problems at compile time like the above. If you refactor, of you swap arguments, you will know only at runtime
  • Class.newInstance仅适用于无参数构造函数。如果没有可用,ReflectiveOperationException则在运行时抛出
  • 反射构造函数不会像上面那样在编译时突出问题。如果你重构,你交换参数,你只会在运行时知道

If I was the author of C#, I would have introduced the ability to specify one or more constructor constraints that are easy to verify at compile time (so I can require for example a constructor with string,stringparams). But the last one is speculation

如果我是 C# 的作者,我会引入指定一个或多个在编译时易于验证的构造函数约束的能力(因此我可以要求例如带有string,string参数的构造函数)。但最后一个是猜测