What is the concept of erasure in generics in Java?

前端 未结 8 693
误落风尘
误落风尘 2020-11-22 01:45

What is the concept of erasure in generics in Java?

相关标签:
8条回答
  • 2020-11-22 01:51

    Complementing the already-complemented Jon Skeet answer...

    It has been mentioned that implementing generics through erasure leads to some annoying limitations (e.g. no new T[42]). It has also been mentioned that the primary reason for doing things this way was backwards compatibility in the bytecode. This is also (mostly) true. The bytecode generated -target 1.5 is somewhat different from just de-sugared casting -target 1.4. Technically, it's even possible (through immense trickery) to gain access to generic type instantiations at runtime, proving that there really is something in the bytecode.

    The more interesting point (which has not been raised) is that implementing generics using erasure offers quite a bit more flexibility in what the high-level type system can accomplish. A good example of this would be Scala's JVM implementation vs CLR. On the JVM, it is possible to implement higher-kinds directly due to the fact that the JVM itself imposes no restrictions on generic types (since these "types" are effectively absent). This contrasts with the CLR, which has runtime knowledge of parameter instantiations. Because of this, the CLR itself must have some concept of how generics should be used, nullifying attempts to extend the system with unanticipated rules. As a result, Scala's higher-kinds on the CLR are implemented using a weird form of erasure emulated within the compiler itself, making them not-entirely-compatible with plain-old .NET generics.

    Erasure may be inconvenient when you want to do naughty things at runtime, but it does offer the most flexibility to the compiler writers. I'm guessing that's part of why it's not going away any time soon.

    0 讨论(0)
  • 2020-11-22 01:54

    There are good explanations. I only add an example to show how the type erasure work with a decompiler.

    Original class,

    import java.util.ArrayList;
    import java.util.List;
    
    
    public class S<T> {
    
        T obj; 
    
        S(T o) {
            obj = o;
        }
    
        T getob() {
            return obj;
        }
    
        public static void main(String args[]) {
            List<String> list = new ArrayList<>();
            list.add("Hello");
    
            // for-each
            for(String s : list) {
                String temp = s;
                System.out.println(temp);
            }
    
            // stream
            list.forEach(System.out::println);
        }
    }
    

    Decompiled code from its bytecode,

    import java.io.PrintStream;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.Objects;
    import java.util.function.Consumer;
    
    public class S {
    
       Object obj;
    
    
       S(Object var1) {
          this.obj = var1;
       }
    
       Object getob() {
          return this.obj;
       }
    
       public static void main(String[] var0) {
    
       ArrayList var1 = new ArrayList();
       var1.add("Hello");
    
    
       // for-each
       Iterator iterator = var1.iterator();
    
       while (iterator.hasNext()) {
             String string;
             String string2 = string = (String)iterator.next();
             System.out.println(string2);
       }
    
    
       // stream
       PrintStream printStream = System.out;
       Objects.requireNonNull(printStream);
       var1.forEach(printStream::println);
    
    
       }
    }
    
    0 讨论(0)
  • 2020-11-22 01:58

    As I understand it (being a .NET guy) the JVM has no concept of generics, so the compiler replaces type parameters with Object and performs all the casting for you.

    This means that Java generics are nothing but syntax sugar and don't offer any performance improvement for value types that require boxing/unboxing when passed by reference.

    0 讨论(0)
  • 2020-11-22 02:00

    Why use Generices

    In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types. ode that uses generics has many benefits over non-generic code:

    • Stronger type checks at compile time.
    • Elimination of casts.
    • Enabling programmers to implement generic algorithms.

    What is Type Erasure

    Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

    • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
    • Insert type casts if necessary to preserve type safety.
    • Generate bridge methods to preserve polymorphism in extended generic types.

    [N.B.]-What is bridge method? To shortly, In the case of a parameterized interface such as Comparable<T>, this may cause additional methods to be inserted by the compiler; these additional methods are called bridges.

    How Erasure Works

    The erasure of a type is defined as follows: drop all type parameters from parameterized types, and replace any type variable with the erasure of its bound, or with Object if it has no bound, or with the erasure of the leftmost bound if it has multiple bounds. Here are some examples:

    • The erasure of List<Integer>, List<String>, and List<List<String>> is List.
    • The erasure of List<Integer>[] is List[].
    • The erasure of List is itself, similarly for any raw type.
    • The erasure of int is itself, similarly for any primitive type.
    • The erasure of Integer is itself, similarly for any type without type parameters.
    • The erasure of T in the definition of asList is Object, because T has no bound.
    • The erasure of T in the definition of max is Comparable, because T has bound Comparable<? super T>.
    • The erasure of T in the final definition of max is Object, because T has bound Object & Comparable<T> and we take the erasure of the leftmost bound.

    Need to be careful when use generics

    In Java, two distinct methods cannot have the same signature. Since generics are implemented by erasure, it also follows that two distinct methods cannot have signatures with the same erasure. A class cannot overload two methods whose signatures have the same erasure, and a class cannot implement two interfaces that have the same erasure.

        class Overloaded2 {
            // compile-time error, cannot overload two methods with same erasure
            public static boolean allZero(List<Integer> ints) {
                for (int i : ints) if (i != 0) return false;
                return true;
            }
            public static boolean allZero(List<String> strings) {
                for (String s : strings) if (s.length() != 0) return false;
                return true;
            }
        }
    

    We intend this code to work as follows:

    assert allZero(Arrays.asList(0,0,0));
    assert allZero(Arrays.asList("","",""));
    

    However, in this case the erasures of the signatures of both methods are identical:

    boolean allZero(List)
    

    Therefore, a name clash is reported at compile time. It is not possible to give both methods the same name and try to distinguish between them by overloading, because after erasure it is impossible to distinguish one method call from the other.

    Hopefully, Reader will enjoy :)

    0 讨论(0)
  • 2020-11-22 02:02

    To complete the already very complete Jon Skeet's answer, you have to realize the concept of type erasure derives from a need of compatibility with previous versions of Java.

    Initially presented at EclipseCon 2007 (no longer available), the compatibility included those points:

    • Source compatibility (Nice to have...)
    • Binary compatibility (Must have!)
    • Migration compatibility
      • Existing programs must continue to work
      • Existing libraries must be able to use generic types
      • Must have!

    Original answer:

    Hence:

    new ArrayList<String>() => new ArrayList()
    

    There are propositions for a greater reification. Reify being "Regard an abstract concept as real", where language constructs should be concepts, not just syntactic sugar.

    I should also mention the checkCollection method of Java 6, which returns a dynamically typesafe view of the specified collection. Any attempt to insert an element of the wrong type will result in an immediate ClassCastException.

    The generics mechanism in the language provides compile-time (static) type checking, but it is possible to defeat this mechanism with unchecked casts.

    Usually this is not a problem, as the compiler issues warnings on all such unchecked operations.

    There are, however, times when static type checking alone is not sufficient, like:

    • when a collection is passed to a third-party library and it is imperative that the library code not corrupt the collection by inserting an element of the wrong type.
    • a program fails with a ClassCastException, indicating that an incorrectly typed element was put into a parameterized collection. Unfortunately, the exception can occur at any time after the erroneous element is inserted, so it typically provides little or no information as to the real source of the problem.

    Update July 2012, almost four years later:

    It is now (2012) detailed in "API Migration Compatibility Rules (Signature Test)"

    The Java programming language implements generics using erasure, which ensures that legacy and generic versions usually generate identical class files, except for some auxiliary information about types. Binary compatibility is not broken because it is possible to replace a legacy class file with a generic class file without changing or recompiling any client code.

    To facilitate interfacing with non-generic legacy code, it is also possible to use the erasure of a parameterized type as a type. Such a type is called a raw type (Java Language Specification 3/4.8). Allowing the raw type also ensures backward compatibility for source code.

    According to this, the following versions of the java.util.Iterator class are both binary and source code backward compatible:

    Class java.util.Iterator as it is defined in Java SE version 1.4:
    
    public interface Iterator {
        boolean hasNext();
        Object next();
        void remove();
    }
    
    Class java.util.Iterator as it is defined in Java SE version 5.0:
    
    public interface Iterator<E> {
        boolean hasNext();
        E next();
        void remove();
    }
    
    0 讨论(0)
  • 2020-11-22 02:05

    It's basically the way that generics are implemented in Java via compiler trickery. The compiled generic code actually just uses java.lang.Object wherever you talk about T (or some other type parameter) - and there's some metadata to tell the compiler that it really is a generic type.

    When you compile some code against a generic type or method, the compiler works out what you really mean (i.e. what the type argument for T is) and verifies at compile time that you're doing the right thing, but the emitted code again just talks in terms of java.lang.Object - the compiler generates extra casts where necessary. At execution time, a List<String> and a List<Date> are exactly the same; the extra type information has been erased by the compiler.

    Compare this with, say, C#, where the information is retained at execution time, allowing code to contain expressions such as typeof(T) which is the equivalent to T.class - except that the latter is invalid. (There are further differences between .NET generics and Java generics, mind you.) Type erasure is the source of many of the "odd" warning/error messages when dealing with Java generics.

    Other resources:

    • Oracle documentation
    • Wikipedia
    • Gilad Bracha's Java generics guide (PDF - highly recommended; link may need to change periodically)
    • Angelika Langer's Java Generics FAQ
    0 讨论(0)
提交回复
热议问题