Typparameter ohne Varianz in Java (Typparameter ohne Varianz in Java), Lektion, Seite 722856
https://www.purl.org/stefan_ram/pub/typparameter_ohne_varianz_java (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram

Typparameter ohne Varianz in Java

Diese Einführung in Typparameter in Java greift nur einige Aspekte beispielhaft heraus. Sie ist nicht als systematische und vollständige Einführung in dieses Thema gemeint.

Typparameter von Methoden

Typparameter können nie elementare Typen darstellen, sondern immer nur Referenztypen. Letztendlich stellen sie eine glorreiche Verzierung für etwas dar, das dann vom Compiler letztendlich als »java.lang.Object« übersetzt wird, denn die genauen Typen von Typparametern werden nur vom Compiler zur Prüfung eines Programms verwendet, aber dann meistens mit »java.lang.Object« übersetzt!

Als einfaches Beispiel betrachten wir eine Methode, die ihr Argumentobjekt wieder zurückgibt:

Main.java

public final class Main
{

public static java.lang.Object id( final java.lang.Object arg )
{ return arg; }

public static void main( final java.lang.String args[] )
{ java.lang.System.out.println( id( "abc" ).length() ); }}

transcript
Main.java:8: error: cannot find symbol
{ java.lang.System.out.println( id( "abc" ).length() ); }}
^
symbol: method length()
location: class Object
1 error

Die Information, daß das Argument den Typ »java.lang.String« hat, ist verloren gegangen.

Durch Festlegung des Typs java.lang.String, können wir das Problem lösen, aber die Methode ist nun nicht mehr so universell, da sie nur noch mit java.lang.String-Objekten aufgerufen werden kann.

Main.java

public final class Main
{

public static java.lang.String id( final java.lang.String arg ){ return arg; }
public static java.lang.Integer id( final java.lang.Integer arg ){ return arg; }
public static java.lang.Double id( final java.lang.Double arg ){ return arg; }

public static void main( final java.lang.String args[] )
{ java.lang.System.out.println( Main.id( "abc" ).length() ); }}

transcript
3

Durch einen Typparameter können wir die Methode am Orte ihrer Deklaration weiterhin universell belassen und dann erst beim Aufruf auf java.lang.String oder – bei einem anderen Aufruf – auf java.lang.Integer festlegen.

Main.java

public final class Main
{

public static< T >T id( final T arg )
{ return arg; }

public static void main( final java.lang.String args[] )
{ java.lang.System.out.println( Main.< java.lang.String >id( "abc" ).length() );
/* a.lang.System.out.println( < java.lang.String >id( "abc" ).length() ); // geht nicht */
java.lang.System.out.println( Main.< java.lang.Integer >id( new java.lang.Integer( 2 )).doubleValue() ); }}

transcript
3

Wir können das Typargument auch weglassen, es wird dann vom Compiler hergeleitet (als java.lang.String oder java.lang.Integer). Dadurch wird dann auch der Rückgabetyp festgelegt.

Main.java

public final class Main
{

public static< T >T id( final T arg )
{ return arg; }

public static void main( final java.lang.String args[] )
{ java.lang.System.out.println( Main.< java.lang.String >id( "abc" ).length() );
java.lang.System.out.println( Main.< java.lang.Integer >id( new java.lang.Integer( 2 )).doubleValue() );
java.lang.System.out.println( id( "abc" ).length() );
java.lang.System.out.println( id( new java.lang.Integer( 2 )).doubleValue() ); }}

transcript
3
2.0
3
2.0

Falls wir in der Methode eine spezielle Methode aufrufen, kann und sollte der Typparameter entsprechend eingeschränkt werden.

Main.java

public final class Main
{

public static< T extends java.lang.CharSequence >void printLength( final T arg )
{ java.lang.System.out.println( arg.length() ); }

public static void main( final java.lang.String args[] )
{ printLength( "abcde" ); }}

transcript
5

Mit einer Und-Verknüpfung können wir ganz genau sagen, welche Schnittstellen wir alle benötigen.

Main.java

public final class Main
{

public static< T extends java.lang.CharSequence & java.lang.Comparable< T >>
void printLengthIfSame( final T arg, final T arg1 )
{ if( arg.compareTo( arg1 )== 0 )
java.lang.System.out.println( arg.length() ); }

public static void main( final java.lang.String args[] )
{ printLengthIfSame( "abcde", "abcde" ); }}

transcript
5

(Eine Oder-Verknüpfung zweier Typen kann durch Überladung gelöst werden.)

Typparameter von Klassen

Auch Klassen können Typparameter haben.

Main.java

class Holder< T >
implements java.util.function.Supplier< T >
{ T object;
public Holder( final T object ){ this.object = object; }
public T get(){ return this.object; }}

public final class Main
{

public static void main( final java.lang.String args[] )
{ { final Holder< java.lang.String >holder = new Holder< java.lang.String >( "defg" );
java.lang.System.out.println( holder.get() ); }
{ final Holder< java.lang.String >holder = new Holder<>( "hijk" );
java.lang.System.out.println( holder.get() ); }}}

transcript
defg
hijk

Für die Typparameter von Klassen gilt das oben zu den Typparameter von Methoden Gesagte (extends, &) entsprechend.

In der Methode-Holder sind keine Operationen, wie beispielsweise »new T()« möglich, weil diese die Kenntnis des speziellen Argumenttyps zur Laufzeit voraussetzen. Diese Information steht zur Laufzeit aber in der Regel nicht zur Verfügung.

Typlöschung

Die Information über das konkrete Typargument dient nur dem Compiler zur Typprüfung. Sie ist zur Laufzeit nicht verfügbar!

Main.java

class Wrapper< T >
{ T t;
Wrapper(){ this.t = new T(); }
T get(){ return t; }}

public final class Main
{

public static void main( final java.lang.String args[] )
{ final Wrapper< java.lang.Object >wrapper
= new Wrapper< java.lang.Object >(); }}

transcript
Main.java:3: error: unexpected type
Wrapper(){ this.t = new T(); }
^
required: class
found: type parameter T
where T is a type-variable:
T extends Object declared in class Wrapper
1 error

Tricks gegen die Typlöschung

Bei anonymen inneren Klassen, stehen aber gewisse Informationen über die Typargumente ihrer Oberklasse zur Verfügung, so daß man hier den Typ zur Laufzeit durch komplizierte Abfragen erhalten kann. (Dieses Beispiel dient an dieser Stelle des Kurses nur der Unterhaltung und soll nicht als Empfehlung verstanden werden, davon bei der Programmierung Gebrauch zu machen.)

Gafter's Device, http://gafter.blogspot.com/2006/12/super-type-tokens.html

Main.java

class B< T >{}

class C extends B< java.lang.Double >{}

public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( ( ( java.lang.reflect.ParameterizedType )C.class.getGenericSuperclass() ).
getActualTypeArguments()[ 0 ]); }}

transcript
class java.lang.Double
Main.java
class MyWrapper< T >
{ T mio;
@java.lang.SuppressWarnings( "unchecked" )
MyWrapper() throws java.lang.Throwable
{ mio =( T )
( ( ( java.lang.Class )
( ( ( java.lang.reflect.ParameterizedType )
this.getClass().getGenericSuperclass() ).
getActualTypeArguments()[ 0 ])).newInstance() ); }}

public final class Main
{ public static void main( final java.lang.String args[] ) throws java.lang.Throwable
{
{ MyWrapper< java.lang.String >a = new MyWrapper< java.lang.String >(){};
java.lang.System.out.println( "ax = " + a.mio );
java.lang.System.out.println( a.mio.getClass() ); }

{ MyWrapper< java.lang.Object >a = new MyWrapper< java.lang.Object >(){};
java.lang.System.out.println( "ax = " + a.mio );
java.lang.System.out.println( a.mio.getClass() ); }}}
transcript
ax = 
class java.lang.String
ax = java.lang.Object@1db9742
class java.lang.Object
Aussprachehinweise
super ˈsupɚ (sd)

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram722856 stefan_ram:722856 Typparameter ohne Varianz in Java Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd722856, slrprddef722856, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/typparameter_ohne_varianz_java