2009-07-14 20 views
4

Ich habe eine Java-Schnittstelle definiert, die die Fähigkeit eines Objekts darstellt, sich selbst zu kopieren (nein, ich möchte nicht Cloneable verwenden, aber danke für den Vorschlag ;-). Es ist generic:Casting auf einen rekursiv begrenzten Typ

public interface Copiable<T> { 
    T copy(); 
} 

Ein Objekt nur Kopien seiner eigenen Art machen:

public class Foo implements Copiable<Foo> { 
    Foo copy() { return new Foo(); } 
} 

Nun, ich Class<?> Objekte aus einer nicht-generischen API bin Lesen (Hibernate-Metadaten, wenn das von Interesse für jedermann), und wenn sie Copiable sind, möchte ich sie mit einer meiner Komponenten registrieren. Die register Methode hat die folgende Signatur:

<T extends Copiable<T>> register(Class<T> clazz); 

Mein Problem ist, wie ich die Klasse geworfen haben, bevor es an die Methode übergeben? Was ich gerade mache ist:

Class<?> candidateClass = ... 
if (Copiable.class.isAssignableFrom(candidateClass)) { 
    register(candidateClass.asSubclass(Copiable.class)); 
} 

Das macht was ich will, aber mit einer Compiler-Warnung. Und ich glaube nicht, dass ich in der Lage sein werde, die rekursive Grenze in einem Laufzeit-Cast auszudrücken.

Gibt es eine Möglichkeit, meinen Code zu überarbeiten, um es typsicher zu machen?

Dank

Antwort

0

Ein Runtime-cast <? extends Copiable<?> ausfällt (im register() Anruf), weil es keine Möglichkeit gibt, zu erklären, dass die erste und die zweiten ?? gleich sind (unbekannt) Sache.

Der asSubclass() Anruf gibt Ihnen eine Warnung Compiler für so ziemlich dem gleichen Grund - weil die Generika gelöscht wurden, gibt es keine Möglichkeit zur Laufzeit zu beweisen, dass ein Copiable ist ein Copiable<AnythingInParticular>. Du hast bewiesen, dass es ein Copiable ist, aber du hast nicht bewiesen, dass es ein Copiable<Itself> ist.

Ich denke, das Beste, auf das Sie hoffen können, ist, die Warnungen zu einer unordentlichen Methode zu lokalisieren, und @SuppressWarnings("unchecked") und einige Anmerkungen hinzuzufügen.

Wissen Sie, dass dies zur Laufzeit funktioniert? Ich habe das Gefühl, dass der Compiler hier die Wahrheit sagt. Erweitern die Klassen, die Sie von Hibernate erhalten, tatsächlich Copiable, oder deklarieren sie einfach Methoden, die der Copiable-Schnittstelle entsprechen? Wenn es das letztere ist, dann wird Java immer eine falsche Sprache sein. Ihre isAssignableFrom() wird sowieso immer false zurückgeben.

+0

Ja, das bestätigt, was ich erahnt habe. Dann nehme ich an, dass, wenn eine Klasse Copiable implementiert, implementiert Copiable , und ich kann die Warnung ignorieren. Dies ist akzeptabel, da alle Klassen unter meiner Kontrolle stehen. Ich akzeptiere diese Antwort, danke! – Olivier

+0

Und ja, das funktioniert zur Laufzeit. Die Klassen, die ich von Hibernate bekomme, sind die Klassen meiner Entitäten, in denen ich die Schnittstelle deklariere. – Olivier

0

Es gibt keine Möglichkeit, nicht-generische API generic zu werfen und keine Warnung erhalten. Sie können @SuppressWarnings("unchecked") Annotation verwenden, um es zu unterdrücken.
Class<?> ist in diesem Fall auch ziemlich sinnlos - Sie können stattdessen nicht-generische Class candidateClass verwenden.

+0

Eigentlich gibt es einen Weg: Wenn der Typ Parameter meiner Methode nicht rekursiv war, zum Beispiel , würde mir candidateClass.asSubclass (SomeInterface.class) den richtigen Typ geben. Sicher kann asSubclass eine ClassCastException zur Laufzeit auslösen, aber das würde nie passieren, wenn ich den Typ mit isAssignableFrom => no warning testen würde. Meine Frage ist, ob ich das mit einem rekursiven Typparameter tun kann. – Olivier

+0

BTW, Klasse ist nicht sinnlos: Wenn ich Klasse I verwendet, würde eine andere Warnung für die Verwendung eines Rohtyps erhalten. – Olivier

+0

Das ist nicht was ich meinte. Sie haben in Ihrer Frage gesagt, dass Sie "candidateClass" von einer nicht generischen API erhalten. Oder habe ich das falsch verstanden? Wenn es tatsächlich nicht generisch ist, dann ** per Definition ** gibt es keine Möglichkeit, es ohne Warnung in Generic zu konvertieren. Soweit was ich mit "sinnlos" meinte, hat "Klasse " dieselbe semantische Bedeutung wie nicht-generische "Klasse" und wenn Sie die Warnung trotzdem unterdrücken wollen, können Sie nur die letztere verwenden. – ChssPly76

0

Vielleicht könnten Sie das super type token Paradigma irgendwie anwenden? Warum müssen Sie Ihr Register einschränken()? Ich denke, das Problem ist, dass Sie Class.forName verwenden und es ist zu viel eine Laufzeit Sache.

+0

Nice Link in der Tat, aber ich denke nicht, dass es mein Problem lösen wird. Ich beschränke Register, naja, um eine Beschränkung auf die Art von Klassen auszudrücken, die akzeptiert werden :-) Zugegeben, ich werde wahrscheinlich eine Warnung unterdrücken, aber im Client-Code. Ich meine, wenn ich einen Aufruf schreibe, um mich mit einem Klassenliteral zu registrieren, liefert es die richtige Typsicherheit. Das Problem ist, dass mein Client die Klasse erst zur Laufzeit kennt. – Olivier