2017-06-08 4 views
3

Ich habe folgenden Code und es verhält sich nicht wie ich denke. Ich habe die Kommentare inline gesetzt, was passiert und was erwartet wird.Generics und Vererbung Ecke Fall

Kann mir bitte jemand helfen, dieses Verhalten zu verstehen.

+2

Well-Klasse ist nicht die gleiche Art wie Klasse

+0

Die Lösung wahrscheinlich einen Typ I 'I ' hinzuzufügen ist und haben 'm (Klasse ' in I –

Antwort

1

Ich versuche zu erklären, was los ist, Schritt für Schritt:

Klasse DC<Class<String>> erstreckt, die als von der Definition der Klasse C die Methode mit der Signatur void m(Class<String> arg) zur Compile-Zeit führt. Durch Typlöschen werden die Informationen zum Typparameter String nach dem Übersetzen gelöscht. Daher erzeugt der Bytecode eine Methode void m(Class arg), die durch die Klasse C definiert wird.

Zusätzlich Klasse D implementiert Schnittstelle I die D ein Verfahren m(Class arg) zu implementieren erfordert, die zum Zeitpunkt der Kompilierung von dem Verfahren unterscheidet m(Class<String> arg) aufgrund der Typ-Parameter. Daher müssen Sie eine Implementierung für die frühere Methode bereitstellen oder D abstract deklarieren.

Wenn Sie bieten eine Implementierung für die Methode m(Class arg) durch Schnittstelle deklariert I Sie in der Tat die Methode nach Klasse definiert außer Kraft setzen C, weil beide die gleiche Signatur haben nach Typ Löschung angewendet wird.

Die Dinge ändern sich, wenn Sie Klasse erweitern C<Collection<String>> machen. In diesem Fall führt die Klasse C eine Methode mit der Signatur void m(Collection<String> arg) zur Kompilierungszeit ein, die sich nach dem Löschen des Typs als Bytecode in void m(Collection args) ändert. Aber jetzt hat die Methode void m(Class arg), die von der Schnittstelle I deklariert wurde, eine andere Signatur und wird daher nicht überschreiben, sondern die Überladungsmethode m, die durch die Klasse C definiert ist.

Hier ist ein einfaches Java-Beispiel:

class C<T> { 
    void m(T arg) { 
     System.out.println("Method [m] of class [C] called"); 
    } 
} 

class D extends C<Class<String>> implements I { 
    @Override 
    public void m(Class arg) { 
     System.out.println("Method [m] of class [D] called"); 
    } 
} 

class DD extends C<Collection<String>> implements I { 

    @Override 
    public void m(Class arg) { 
     System.out.println("Method [m] of class [DD] called"); 
    } 
} 

und hier eine Folge von Anrufen und die Ausgabe:

public static void main(String[] args) { 
    new D().m(D.class); 
    new D().m((Class<String>) null); 

    new DD().m(DD.class); 
    new DD().m(new ArrayList<String>()); 
} 

Ausgabe

Method [m] of class [D] called 
Method [m] of class [D] called 
Method [m] of class [DD] called 
Method [m] of class [C] called. 
0

erbt nichts von I, weil es keine Standardmethode hat, nur eine Methodensignatur, für die Sie tatsächlich keine Implementierung bereitstellen. Wie @RC darauf hinweist, ist der Typ Ihres m so unterschiedlich, dass er nicht als gültige Implementierung gilt. Um den Fehler bezüglich eines Konflikts zu erhalten, müssten Sie tatsächlich eine in Konflikt stehende Implementierung hinzufügen.

In E markieren Sie Ihre Klasse als abstrakt, so dass es in Ordnung ist, die Methode nicht wie in der Schnittstelle implementiert zu implementieren. Sobald Sie in einer Klasse arbeiten, die E erweitert, lösen Sie den Fehler bei konfliktbehafteten Implementierungen aus, da nach dem Typ löschen die Typen identisch wären. Im Grunde haben Sie nur das Problem von D zu was auch immer erweitert wird E verschoben.

0

Ich erwarte folgende Diskussion in jdk8.

Im Code:

class D extends C<Class<String>> implements I { 

}

sollte die Schnittstelle I wie folgt implementieren:

class D extends C<Class<String>> implements I { 

    @Override 
    public void m(Class arg) { 

    } 
} 

Mal sehen, warum sollte die Methode in der I Schnittstelle implementieren .Nach der JLS8.4.8.1:

An instance method mC declared in or inherited by class C, overrides from C another 
method mI declared in an interface I, iff all of the following are true: 
• I is a superinterface of C. 
• mI is an abstract or default method. 
• The signature of mC is a subsignature (§8.4.2) of the signature of mI. 

In dem obigen Code, die mCvoid m(Class<String> arg) in Klasse D ist, die mIvoid m(Class arg)

ist Und siehe die Beschreibung von subsignature:

And the `JLS8.4.2` explain the subsignature between two method: 

The signature of a method m1 is a subsignature of the signature of a method m2 if 
either: 
• m2 has the same signature as m1, or 
• the signature of m1 is the same as the erasure (§4.6) of the signature of m2. 

Nach oben Beschreibung ist die void m(Class<String> arg nicht Untersignatur von void m(Class arg), aber die void m(Class arg) ist Subsignatur von void m(Class<String> arg. So sollte die Klasse die Schnittstelleimplementierenexplizit.

Siehe folgenden Code, es zu erklären:

class C { 
    public void m(Class arg) { 
    } 
} 

interface I<T> { 
    void m(T arg); 
} 

class D extends C implements I<Class<String>> { 

} 

es ok kompilieren.

Die zweite, Warum es nicht Methode widerstreit ist, wegen des JLS8.4.8 das Erbe der Klasse erklären:

A class C inherits from its direct superclass all concrete methods m (both static and instance) of the superclass for which all of the following are true: 
• m is a member of the direct superclass of C. 
• m is public, protected, or declared with package access in the same package as C. 
• No method declared in C has a signature that is a subsignature (§8.4.2) of the signature of m. 

Nach oben Beschreibung der Methode void m(T arg) mit Class<String> Parameter von T-void m(Class<String> arg) übersetzt werden.Und das umgewandelte Verfahren wird zu void m(Class arg) gelöscht, dieses Verfahren wird in der Klasse existieren, so dass die Klasse das Verfahren void m(T arg) in Klasse C nicht erbt.

0

Das Problem reduziert werden kann Diese grundlegende Tatsache über Java-Generika, dass es sicher ist, ein insta zuzuweisen ntiated generischer Typ zu Raw-Typ, aber das Gegenteil ist nicht wahr. Beim Überschreiben einer Methode sind unsichere Konvertierungen nicht zulässig. Daher kann Class<String> den rohen Klassentyp nicht überschreiben.

In diesem Fall:

Class a = String.class; //type safe 
    Class<String> b = a; //not type safe