2016-07-01 7 views
8

Ich habe ein einfaches Programm wie folgt aus:Java Generics - allgemeine Klasse mit generischen Funktionserweiterungs

package test; 

public class TestGenericsInheritance { 

    public static void main(String[] args) {} 

    public static abstract class A<Q>{ 

     public void foo2(Q obj){} 
     public abstract <T> void foo(T obj); 
    } 

    public static class C extends A<Object>{ 

     @Override 
     public <T> void foo(T obj) {} 
    } 

    public static class B extends A{ 

     @Override 
     public <T> void foo(T obj) {} 
    } 
} 

wie Sie es absolut nichts sehen. Erstellung dieser Programmdatei auf Java 1.6 und 1.7 mit folgenden Fehlern:

/D:/Projects/.../test/TestGenericsInheritance.java:[24,19] test.TestGenericsInheritance.B is not abstract and do es not override abstract method foo(java.lang.Object) in test.TestGenericsInheritance.A /D:/Projects/.../test/TestGenericsInheritance.java:[27,25] name clash: foo(T) in test.TestGenericsInheritance .B and foo(T) in test.TestGenericsInheritance.A have the same erasure, yet neither overrides the other /D:/Projects/.../test/TestGenericsInheritance.java:[26,9] method does not override or implement a method from a supertype

Klassen C und B ist semantisch identisch, aber Klasse B nicht Methode foo als Implementierung von A # foo nicht erkennt. Um diesen Code konform zu machen Ich habe Methode A # foo in der Klasse B mit folgenden Unterschrift zu implementieren:

public void foo(Object obj) 

Meine Frage ist, warum mein Programm nicht kompiliert? Die generischen Typen Q und T sind vollständig unabhängig, weshalb kann ich die generische Funktion A # foo nur implementieren, wenn ich explizit den generischen Typ Q für die vererbte Klasse A festlege?

Dank

+0

Ich bin in der Lage zu repro '. Darüber hinaus führt die gleichzeitige Verwendung der nicht-generischen Methode und der generischen Methode zu einem Fehler beim Löschen desselben Typs. – 4castle

+0

Eclipse schlägt vor: '' '@Override public void foo (Object obj) {}' '' '' '' B'' 'das macht es tatsächlich kompilieren. (1.8) –

+0

@ 4castle Ihre Bearbeitung hat die Fehlermeldungen verwirrend gemacht, da sie einen Quelldateinamen erwähnen, den Sie entfernt haben. –

Antwort

6

B erweitert die rohe Art A. Da Sie einen unformatierten Typ verwenden, werden alle generischen Informationen gelöscht, nicht nur die Typvariable, die Sie nicht angeben konnten (siehe Section 4.6 of the Java Language Specification). Dies bedeutet, dass A eine Methode void foo(Object obj) hat, während A<SomeType> eine Methode <T> void foo(T obj) hat.

Wenn Sie eine Methode überschreiben, muss sie die gleiche Signatur haben (optional nach dem Typ Löschen der überschriebenen Methode) - interessanterweise kann die überschreibende Methode einen anderen, spezifischeren Rückgabetyp haben. Die Signaturen Ihrer beiden Methoden sind unterschiedlich (Sie müssten in der Überschreibungsmethode Typ löschen). Daher wird Ihr Beispiel nicht kompiliert.

Der Grund Typ Löschung wurde implementiert, wie es ist, ist Abwärtskompatibilität. Die Idee war, dass neuer Code nur Generika verwenden würde und dass nur alter Code rohe Typen verwenden würde. Das Ziel bestand also nicht darin, rohe Typen so komfortabel wie möglich zu gestalten (weil neuer Code sie ohnehin nicht verwenden sollte), sondern um rohe Typen kompatibel zu machen.

Betrachten Sie zum Beispiel die Klasse ArrayList, die in Java 2 (lange vor Generika) eingeführt wurde. Es hatte eine Methode public Object[] toArray(Object[] a). In Java wurden 5 Generika eingeführt, und die Signatur dieser Methode konnte problemlos in public <T> T[] toArray(T[] a) geändert werden (wobei T nicht der Elementtyp ist). Wegen der Art, wie Löschart implementiert wird, für alten Code, der eher ArrayList verwendet (oder unterklassifiziert) als ArrayList<SomeType> blieb die Methodensignatur gleich.

+0

Ich verstehe es immer noch nicht. Wo werden "alle generischen Informationen gelöscht" in der Spezifikation angezeigt? – 4castle

+1

Es heißt: "Typ löscht auch die Signatur (§8.4.2) eines Konstruktors oder einer Methode auf eine Signatur, die keine parametrisierten Typen oder Typvariablen hat." Keine zusätzlichen Bedingungen dort erwähnt. – Hoopje

+0

@Hoopje Vielen Dank für Ihre Erklärung. Ich verstehe, was der Grund für mein Problem ist.Ich sehe jedoch keinen Sinn darin, alle generischen Typen in diesem Fall zu löschen. Ich verstehe, dass der Typparameter gelöscht werden sollte. Aber warum Methodenparameter löschen, wenn sie unabhängig vom Typparameter sind? Können Sie einen Fall angeben, in dem eine solche Löschung notwendig ist? – MAREK