What is Generic Type Casting in Java



The innovations in Java 5 include the generics described in JSR 14. Generics ("genericity", "generic elements") are used in the Java environment to refer to language elements with which classes and methods can be parameterized with type parameters in order to enable type security despite generic programming.

Generics are only evaluated during compilation. The resulting bytecode is downward compatible.

The following text only describes the basics of generics. Further information can be found, for example, at:



content

  1. Simple generics application example
  2. Mixture of code with and without generics
  3. Create a generic class yourself
  4. Create generic method yourself
  5. Type restriction
    Covariance in arrays, invariance in generics, "extends"
  6. Wildcards
    Wildcard types,
    "" versus "",
    "List" versus "List ",
    "List " Versus "List ",
    "List " versus "List ",
    "" versus "",
    Example with "" and ""
  7. Generics with enums
  8. When are raw types needed?
  9. Diamond Operator (from Java 7)



  10. Simple generics application example

    In the simple cases, the syntax of generics consists of a type name that is enclosed in angle brackets and usually comes after a class name to be specified. For example, for a list of strings:

    Without generics:
    With generics:

    The variant with generics does not require a typecast when reading. Even more important: If, for example, an object were added instead of the string, an exception would only be thrown at runtime without generics, while with generics the compiler detects this error during compilation:

    Without generics:
    With generics:

    The addition of type information via generics shown here can only be used on interfaces, classes and methods that are prepared for this, such as,, further collections and own classes.



    Mixture of code with and without generics

    Mixing code with and without generics should be avoided whenever possible, as this can be confusing and lead to errors. But Java allows it, as the following working example shows:

    import java.util. *; public class GenericsTest1 {public static void main (String [] args) {List listeMitGenerics = new ArrayList (); listMitGenerics.add ("Hello1"); methodWithoutGenerics (listWithGenerics); List listeOhneGenerics = new ArrayList (); listWithoutGenerics.add ("Hello2"); methodWithGenerics (listWithoutGenerics); } static void methodOhneGenerics (List lst) {System.out.println (lst.get (0)); } static void methodMitGenerics (List lst) {System.out.println (lst.get (0)); }}

    That will be handed over and vice versa that will be handed over.

    The following example shows which problems a mixture can lead to:

    import java.util. *; public class GenericsTest2 {public static void main (String [] args) {List listWithGenerics = new ArrayList (); List listWithoutGenerics = listWithGenerics; System.out.println (listWithGenerics.getClass () == listWithoutGenerics.getClass ()); // true listWithoutGenerics.add (new Integer (42)); // no compilation error (although actually a string list) String s = listWithGenerics.get (0); // <- ClassCastException at runtime! }}

    While the no object could be added, this can be done with the one declared with the "Raw Type" even though the reference is the same. Although there is no typecast, one is thrown at runtime.

    The reason for this behavior is that the generics information is only available at compile time, but no longer at runtime, as it is missing in the bytecode of the .class file (as the comparison of the two classes in the example shows).



    Create a generic class yourself

    The following example shows the definition and use of the simple generic class:

    public class GenericsTest3 {public static void main (String [] args) {MyGenericClass mgk = new MyGenericClass (); mgk.setAttribut (new Double (3.14)); Double d = mgk.getAttribut (); // without Typecast System.out.println (d + "(" + mgk.getAttribut (). getClass () + ")"); }} class MyGenericClass<T> {private T attribute; void setAttribut ( T attribute) {this.attribute = attribute; } T getAttribut () {return attribute; }}

    The generic class is parameterized with the desired type via. Under this name, the "parameterized type" can be used as a "type variable" in the attributes and methods of the class. Any name (except the reserved ones) could be used instead of "". Single large letters are common, for example for type, for element and / for key / value.

    Interfaces can also be defined with generics:

    interface MeinStackInterfaceMitGenerics<T> {public void push (T obj); public T pop (); }

    Instead of just a single type, several types can also be transferred:

    class MyGenericClass3<T1,T2,T3> {void myMethod (T1 parm1, T2 parm2, T3 parm3) {/ * ... * /}}

    Create generic method yourself

    Not only classes, but also methods can be generic:

    import java.util. *; public class GenericsTest4 {public static void Main(String [] args) {List dd = new ArrayList (); dd.add (1.2); // autoboxing dd.add (2.3); dd.add (3.4); dd.add (4.5); System.out.println ( find index(dd, 3.4)); } static <T> int find index(List<T> mylist, T myObject) {for (int i = 0; i When the generic method is called, the type is not transferred explicitly, but is recognized automatically.

    Several types can also be specified here:

    <T1,T2> T2 myMethod (T1 parm1, T2 parm2) {// ...}

    Type restriction

    Arrays covariance

    Because arrays are covariant and are derived from, is a derivative of. This can lead to runtime errors:

    Number [] a = new Integer [1]; a [0] = new Double (3.14); // <- not a compile error, but a runtime error

    Arrays are "reified": they know and check the element types at runtime.

    Generics invariance

    Although derived from, it is not derived from and the following expression will result in a compile error:

    ArrayList a = new ArrayList (); // <- compile error

    This is known as "invariance": the derivation relationship between type arguments does not carry over to generic classes, there is no covariance with generics. This (unlike arrays) guarantees type safety. Generics are "non-reified" because they are implemented using "Type Erasure": The types are checked at compile time and then the type information is removed.

    The assignment of an integer list to a number list is still possible, it just has to be formulated differently: Either via "bounded type parameter" as shown below or via "upper bounded wildcard" as shown below.

    "extends"

    With the keyword "" the permitted type can be restricted to derivatives of a certain class ("bounded type parameter"):

    import java.math.BigDecimal; public class GenericsTest5 {public static void main (String [] args) {Integer ii = onlyPositive (new Integer (42)); BigDecimal bd = onlyPositive (new BigDecimal (4711)); String s = "bla"; StringBuffer sb = new StringBuffer ("Blubb"); System.out.println (longer text (s, sb)); } static <T extends Number> T onlyPositive (T parm) {return (parm.doubleValue ()> = 0)? parm: zero; } static <T extends CharSequence> T longer text (T parm1, T parm2) {return (parm1.length ()> parm2.length ())? parm1: parm2; }}

    Here, too, several types can be specified:

    <T1 extends Number, T2 extends CharSequence> T2 myMethod (T1 parm1, T2 parm2) {// ...}

    The implementation of additional interfaces can be requested via "&", whereby a class can be, but all other types (,, ...) must be interfaces:

    <T extends [Type1] & [IType2] & [IType3]> T myMethod (T parm) {// ...}

    Recursive use is also possible ("recursive type bound"):

    <T extends [Type1]<T>> T meinMethode (T parm) {// ...} // Preliminary concrete example for a maximum search in a list with "mutually comparable" elements // (you will find an improved version below):<T extends Comparable<T>> T max (List list) {T result = list.get (0); for (T t: list) if (t.compareTo (result)> 0) result = t; return result; }

    Wildcards

    Wildcard types

    There are three types of wildcards and the possibility of combining several types:

    Any type (avoids compiler warning) ("unbounded wildcard")
    Super class of ("lower bounded wildcard")
    Of (Class or interface) derived type ("upper bounded wildcard")
    Subtype of several types (apart from the first type, only interfaces are allowed) ("multiple bounds")

    "" versus ""

    For example

    Cunning<? extends Comparable> myMethod (List<? extends Comparable> list) {/ * ... * /}

    is in many cases comparable to

    <T extends Comparable> Cunning<T> myMethod (List<T> list) {/ * ... * /}

    If the type is still required within the class or method, for example, or if it must be ensured that identical types are used for the parameter and for the return, the second form must be selected. Otherwise the first variant can be used.

    "List" versus "List "

    Even if you do not know the type or want to use different object types, you should not use raw types, such as or, but always generic types, such as, or.

    In the case of raw types, the errors described above and to can easily occur. For generic types (such as and) the compiler (if you do not suppress any warnings) prevents and prevents the use of illegal methods.

    In addition, information can be lost when using raw types. The following lines (with generic lines) work without errors because the class information contains both compilation and runtime information as "Runtime Type Token" (replace e.g. with and with):

    Class<?> c = Class.forName ("mypackage.MyClass"); MyAnnotation a = c.getAnnotation (MyAnnotation.class);

    These lines (without), on the other hand, cannot be compiled:

    Class c = Class.forName ("mypackage.MyClass"); MyAnnotation a = c.getAnnotation (MyAnnotation.class);

    The following example shows how a type-safe heterogeneous container can be used that can store instances of different types in a type-safe manner:

    class Type-safe heterogeneous container {private Map , Object> map = new HashMap , Object> (); public void putObject (Class clss, T obj) {map.put (clss, clss.cast (obj)); } public T getObject (Class clss) {return clss.cast(map.get (clss)); }}

    The "dynamic typecast" with "" in. In is also such a typecast: However, it is only used to avoid errors caused by raw types.

    "List " Versus "List "

    There are important differences between the generic types and. While the following assignment is allowed:

    List <?> wildcardListe = new ArrayList <Integer>();

    the following assignment leads to a compilation error (because of the invariance of generics):

    List <Object> objectListe = new ArrayList <Integer> (); // <- compile error

    There may also be restrictions when using the methods. While the following lines are allowed:

    List <Object> objectListe = new ArrayList <Object>(); objectListe.add (new Integer (42));

    the following lines lead to a compile error:

    List <?> wildcardListe = new ArrayList <Integer>(); wildcardListe.add (new Integer (42));// <- compile error

    So you cannot assign one, but objects can be added via.
    With one it is the other way round: One can be assigned to it, but no objects can be added via.

    This may seem contradictory at first. The reason is as follows:

    • stands for one homogeneous List of elements of the same type (or derivatives of the same superclass) where the type is unknown. Since the type is unknown, an object of perhaps a different type cannot simply be added via, in order not to risk any.
    • stands for one heterogeneous mixed list of items of possibly different types. Since different types are allowed, adding an integer is also allowed.

    "List " versus "List "

    The differences between the generic types and are roughly the same as the above between and. While the following assignment of an integer list is allowed:

    List <? extends Number> extNumberListe = new ArrayList <Integer>();

    The following assignment leads to a compilation error (due to the invariance of generics):

    List <Number> numberListe = new ArrayList <Integer> (); // <- compile error

    There are also restrictions when using the methods. While the following lines are allowed:

    List <Number> numberListe = new ArrayList <Number>(); numberList.add (new Integer (42));

    the following lines lead to a compile error:

    List <? extends Number> extNumberListe = new ArrayList <Integer>(); extNumberListe.add (new Integer (42));// <- compile error

    One cannot be assigned, but objects can be added via.
    With one it is the other way round: One can be assigned to it, but no objects can be added via.
    Even if it initially seems strange that an integer cannot be added to a list, the reason is simple: Since the type is unknown, it could also be a double list, and then an added integer could lead to one .

    "" versus ""

    When should "" and when "" be used?
    The basic rule for input parameters is:
    "" Is used for "consumers" and "" for "producers".

    "" is always a consumer because he uses ("consumes") instances. Therefore "" should always be used. The same would apply to "". If an input parameter acts as an input for a result (i.e. consumes instances), "" can also make sense here (for example in the following stack-pop method: ""). In general, "" is rarely used in your own declarations.

    Wildcards do not make sense for input parameters that are used both as consumer and producer.

    Wildcards should also be avoided if possible with return types.

    In the following example, the method parameter "" is used as a producer because it provides instances. This is why "" is used.

    Example with "" and ""

    Not all classes implementing the interface implement, some implement. The class shown in the following example is an example of this: It implements what makes sense so that, in addition to objects, you can also compare objects with objects.

    The declaration of a generic method shown above does not work for such classes; an improved variant must be used. The following example shows both the original method (which cannot be used here) and the new method with an improved generic declaration. (The programming of the methods is only used to explain the declaration options, in practice it should of course be used instead.)

    In order for the example to compile and run, the line with the compile error must be commented out. The complication error is: "".

    import java.util. *; import java.util.concurrent.atomic.AtomicLong; public class GenericsTest6 {public static void Main(String [] args) {List lngLst = new ArrayList (); lngLst.add (new AtomicLongComparable ()); lngLst.add (new AtomicLongComparable (99)); lngLst.add (new AtomicLongComparable (42)); System.out.println ( findMaximumWildcards(longest)); // works System.out.println ( findMaximumWithoutWildcards(longest)); // <- compile error} // Improved version with wildcards:static > T findMaximumMitWildcards (List list) {T result = list.get (0); for (T t: list) if (t.compareTo (result)> 0) result = t; return result; } // Original version without wildcards:static > T findMaximumOhneWildcards (List list) {T result = list.get (0); for (T t: list) if (t.compareTo (result)> 0) result = t; return result; }} class AtomicLongComparable extends AtomicLong implements Comparable {private static final long serialVersionUID = 0L; public AtomicLongComparable () {super (); } public AtomicLongComparable (long lng) {super (lng); } @Override public int compareTo (AtomicLong o) {return (o == null || get ()> o.get ())? 1: (get ()

    Generics with enums

    Using Generics with Enum classes is similar to using other classes. In the following example, the enum implements the interface. The two methods and demonstrate two different options for method declarations in order to call the calculation method for the example parameter for all enum elements.

    For information on enums, see java-enums.htm.

    import java.util. *; public class GenericsTest7 {public static void main (String [] args) {int i = 42; testAllCalculators1 (MeinEnum.class, i); testAllCalculators2 ( Arrays.asList (MeinEnum.values ​​()), i); } // Version 1:static & Calculator> void testAllCalculators1 (Class calcs, int i) {for (Calculator c: calcs.getEnumConstants () ) System.out.println (c + ":" + i + "->" + c.calculate (i)); } // Variant 2:static void testAllCalculators2 (Collection calcs, int i) {for (Calculator c: calcs) System.out.println (c + ":" + i + "->" + c.calculate (i)); }} interface calculator {int calculate (int i); } enum MeinEnum implements Calculator {ITEM1 {public int calculate (int i) {/ * ... * / return 2 * i; }}, ITEM2 {public int calculate (int i) {/ * ... * / return 7 * i; }}; }

    When are raw types needed?

    Usually, raw types should no longer be used. But there are two exceptions:

    Generics cannot be used in class literals, only raw types:
    "", "", "" and "" are allowed,
    but "" and "" do not.

    The same applies to the operator:
    "" is allowed, but "" is not.
    A corresponding query could look like this:

    if (obj instanceof List) {List lst = (List ) obj; // ...}

    Note that there are methods by which one can get the impression that generics have been forgotten. For example:

    List list = Arrays.asList (new String [] {"abc", "xyz"}); String [] array = list.toArray ();

    A remedy is easily possible with this example:

    List list = Arrays.asList (new String [] {"abc", "xyz"}); String [] array = list.toArray (new String [list.size ()]);

    Diamond Operator (from Java 7)

    For example, a map of string lists is usually defined as follows:

    Map > strListenMap = new HashMap > ();

    As of Java 7, the compiler also allows an abbreviated notation. If all type information is known, the type parameters can be omitted with the new operator and the angle brackets are sufficient (as "Diamond Operator"):

    Map > strListenMap = new HashMap <> ();


    Further topics: other TechDocs | Annotations
    © 2008-2010 Torsten Horn, Aachen