Ich habe eine Klasse, die wie folgt aufgebaut ist:
private Object attribute; // <--- hier T verwenden wäre toll
public <T extends MyObject & MyInterface> MyClass(T obj){
this.attribute = obj;
}
public void doStuff(){
MyObject asObj = (MyObject) attribute; // <-- dann könnte ich mir den Cast sparen...
// nutze attribute als MyObject
MyInterface asInterface = (MyInterface) attribute; // <-- ... und den hier auch
// nutze attribute als MyInterface
}
}```
Der Konstruktor fordert, dass der Parameter von einem Objekt erbt und ein Interface implementiert. Kann ich diese Kombination irgendwie auf das Attribut übertragen? Im Moment ist `attribute` ja vom Typ `Object` und ich caste den Wert halt immer, was mir nicht ganz optimal erscheint.
Hatte überlegt, ob ich die Klasse irgendwie "intern generisch" machen kann, so dass das Attribute auch vom Typ T ist, ohne dass ich das nach außen kommunizieren muss. Was ich also nicht möchte ist, dass ich am Ende `MyClass<T extends ... >` habe, weil man dann die Subtypen ja von außen festlegen könnte. Ich brauche genau das Objekt (MyObject) und das Interface (MyInterface). Darum auch diese umständliche Konstruktor-Signatur.
Die Klasse soll eben Instanzen von MyObject, die MyInterface implementieren entgegennehmen, und sie intern auch so verwalten. Ersteres klappt, zweiteres scheint mir gerade sehr unsauber (Object + Cast).
Gibts da eine bessere Lösung?
Du könntest zwei Member definieren. Einen als MyObject und einen als MyInterface. Ich fände dies aber viel unsauberer, weil der ganze Code dadurch dauerhaft ganze 16 Referenzbytes (4? 8?) pro Instanz mehr belegen würde. Die Casts innerhalb der Methoden sind also okay.
Du kannst doch einfach die Klasse generisch machen.
private T attribute;
public MyClass(T obj) {
this.attribute = obj;
}
}```
Edit: wer lesen kann ist klar im Vorteil...
Welchen Sinn hat es denn, nicht die ganze Klasse generisch zu machen?
Spontan wüßte ich da nichts, außer irgendwelcher auf den ersten Blick etwas fragwürdiger “Augenwischerei” wie einem
class MyObject {}
interface MyInterface {}
class MyClass
{
private static class Holder<T extends MyObject & MyInterface>
{
T t;
Holder(T t)
{
this.t = t;
}
}
private Holder<?> holder;
public <T extends MyObject & MyInterface> MyClass(T obj)
{
this.holder = new Holder<T>(obj);
}
public void doStuff()
{
MyObject asObj = holder.t;
MyInterface asInterface = holder.t;
}
}
(es wird in einem ‘Holder<?>’ gespeichert, aber durch die Deklaration von ‘Holder’ ist sichergestellt, dass dieses ‘?’ immer MyObject&MyInterface ist).
Die Option, es einfach als beides zu speichern, gibt es natürlich (nur der Vollständigkeit halber) auch…
Mal schauen, ob jemand eine geschicktere Lösung hat.
(BTW: Es kann frickelig sein, wenn man unbedingt JEDEN Cast und JEDEN Typ-Check verhindern will. Wenn man in einer Klasse die Verantwortung für die Typsicherheit übernimmt, z.B. durch solche Methoden mit Typparametern, kann ein cast auch OK sein. Aber zugegeben: Ich versuche auch (vielleicht manchmal ZU krampfhaft?) sie zu vermeiden…)
EDIT: Ja, etwas zu langsam. Aber wenn ich das richtig verstanden hatte: Der Typparameter sollte ja gerade NICHT nach draußen sichtbar sein.
[QUOTE=cmrudolph]Welchen Sinn hat es denn, nicht die ganze Klasse generisch zu machen?[/QUOTE]Weil ich mir eigentlich den Generics-Quatsch **außerhalb **der Klasse sparen möchte. Denn dann muss ich ja überall da, wo ich die Klasse verwende den Typen mit ranschreiben, um mir damit das Leben **in **der Klasse einfacher zu machen. Das ist für mich ganz schlechter Stil
@ Marco13
Der Holder ist eine Idee. Den bekomme ich aber auch nur pro „Object-Interface“-Kombination hin, oder? Weil ich hab mehere solcher Klassen und da jedes mal nen Holder zu schreiben… hm.
Also mir persönlich gefällt dieser Holder recht gut. Die 6, 8 bis 2 Bytes, die dadurch mehr entstehen als wenn man eine Instanz 2 mal speichert, rechnen sich dabei schon nach wenigen Objekten. Im Übrigen… man bräuchte in “doStuff” nichtmal mehr carsten sondern kann gleich holder.t (was das denn für’n Name? Klingt wie Elder sowieso. :o)) verwenden. (?)
also ich würde ein weiteres Interface definieren, dass MyInterface erweitert und die Methoden aus MyObject deklariert, auf die ich in meiner Methode zugreifen möchte. das halte ich für sauberer, denn es ist sehr wohl ok nach außen zu kommunizieren, welche Methoden ich gedenke aufzurufen.
@cmrudolp
Hab dein Beispiel mal ausprobiert:
"
[spoiler]```public class GenericTest {
public static void main(final String[] args) {
final Impl i = new Impl();
final Test t = new Test(i);
t.doStuff();
}
public static abstract class SuperClass {public abstract String getString();}
public static interface Interfaze {public int getInt();}
public static class Impl extends SuperClass implements Interfaze {
public int getInt() {return 42;}
public String getString() {return "Sinn des Lebens";}
}
public static class Test {
private static abstract class Proxy extends SuperClass implements Interfaze {}
private final Proxy p;
public <C extends SuperClass & Interfaze> Test(final C param) {this.p = (Proxy) param;}
public void doStuff() {
System.out.println("INT=" + this.p.getInt());
System.out.println("STR=" + this.p.getString());
}
}
}```[/spoiler]
Und bekomme (logischerweise) folgenden Fehler:
Exception in thread "main" java.lang.ClassCastException: GenericTest$Impl cannot be cast to GenericTest$Test$Proxy
at GenericTest$Test.<init>(GenericTest.java:57)
at GenericTest.main(GenericTest.java:18)
Das ist ungefähr das gleiche, wiepublic static main(String[] args){f(new Double(2));} public static void f(Number n){ Number n2 = (Integer)n; }
[QUOTE=Timothy_Truckle;61312]also ich würde ein weiteres Interface definieren, dass MyInterface erweitert und die Methoden aus MyObject deklariert, auf die ich in meiner Methode zugreifen möchte. das halte ich für sauberer, denn es ist sehr wohl ok nach außen zu kommunizieren, welche Methoden ich gedenke aufzurufen.[/QUOTE]Ich benötige aber nicht einige Methoden, ich benötige den Typ MyObject, da ich die mir übergebenen Objekte an andere Klassen weiterreiche, die mit Objekten vom Typ MyObject arbeiten.
Ich habe das Beispiel zu trivial gestaltet und mit Object getestet… Das mag der Compiler dann nämlich. Es hatte mich ehrlich gesagt auch schon gewundert dass es funktioniert…
[QUOTE=cmrudolph]Ich habe das Beispiel zu trivial gestaltet und mit Object getestet… Das mag der Compiler dann nämlich. Es hatte mich ehrlich gesagt auch schon gewundert dass es funktioniert…[/QUOTE]Mich auch. Siehe dazu mein KKSB in meinem letzen Post.
Ich betone nochmal sicherheitshalber, dass ich den “Holder” als fragwürdige “Augenwischerei” bezeichnet hatte. Es mag zwar wie ein “geschicker” Weg sein, das Ziel der cast-Freiheit zu erreichen, aber ich finde, man gewinnt dadurch nicht so viel.
Die anderen Vorschläge, das ganze durch eine “neue Klassenhierarchie” abzubilden (durch eine gemeinsame Oberklasse oder ein Interface, was sowohl MyObject als auch MyInterface abdeckt) sind etwas schwierig allgemein anwendbar .
Also, anhand der bisherigen Beschreibung würde ich wohl auch mal eine pragmatische Lösung in Betracht ziehen…:
[QUOTE=Marco13]Also, anhand der bisherigen Beschreibung würde ich wohl auch mal eine pragmatische Lösung in Betracht ziehen…[/QUOTE]:idea: Getter :cower: na klar. Halte ich für ne super Idee
[QUOTE=Marco13;61365]Vielleicht würde es schon helfen, wenn man einen konkreteren Fall kennte, wo diese Fragestellung auftritt…[/QUOTE]Mein Anwendungsfall ist folgender: Ich habe eigene Gui-Elemente, die von JComponent erben und Interfaces wieinterface GuiValueComponent<T>{ T getValue(); } implementieren.
Dann gibt es Validatoren, die solche JComponent-Interface-Kombinationen entgegennehmen, um die Eingabedaten zu prüfen (DateValidator, StringValidator, etc… ). Tritt ein Fehler auf, so übergibt der Validator die fehlerhaften JComponents an eine Result-Instanz, damit diese in der Gui entsprechend hervorgehoben werden können.
Ein Validator sieht dann in etwas so aus:
private Object toCheck;
//config attributes
public <T extends JComponent & GuiValueComponent<Date>> DateValidator(T toCheck){
this.toCheck = toCheck;
}
public ValidationResult validate(){
Date date = ((GuiValueComponent<Date>)toCheck).getValue();
boolean valid = // check date ...
return valid ? new ValidationResult(true) : new ValidationResult(false, (JComponent) toCheck);
}```
Ahja OK, das könnte Sinn machen. (Eigentlich off-topic, aber … bei einer ähnlichen Struktur hatte ich, übertragen auf diesen Anwendungsfall, mal sowas gemacht wie
interface GuiValueSource<C extends Component, T> { T getValue(C c); }
class DateSpinnerGuiValueSource implements GuiValueSource<JSpinner, Date> { ... }
weil man es sich damit spart, neue Klassen zu definieren, die von den Components erben, aber… ob das eine nachhaltige Lösung ist, muss sich noch rausstellen, ist alles noch seehr experimentell, ging aber um sowas ähnliches wie GitHub - aeremenok/swing-formbuilder: Generates swing components from java beans at runtime , was deinem Anwendungsfall vielleicht auch nicht ganz unähnlich ist).
Wie auch immer, ein paar Optionen gibt es ja, kannst ja mal schreiben, welche du gewählt hast, und wie viel oder wenig du im Nachhinein drüber geflucht hast
Aktuell werde ich mal die Version mit den Gettern einsetzen, da mir diese am wenigsten Overhead erzeugt und auch am simpelsten ist (KISS).
Ziel meines Interfaces war ja, dass die Gui-Element selbst den Wert zurück geben und ich eben keine Klasse benötige, die das für sie macht. Allerdings wäre es auch eine Lösung, (ähnlich wie bspw. bei Comparable) die implementierende Klasse als Generischen Parameter zu verlangen und eine Methode public JComponent asComponent(){return this;} zu erzwingen. Aber auch dann hätte ich diese zusätzliche Methode, und das Problem der Validatoren verwandelt sich zum Probelm des Gui-Elements.
Daher bleibe ich jetzt erstmal bei den privaten Getter in den Validatoren. Wird sich zeigen, ob das eine gute Idee war. Und solange ich kein “Best Practice: Validate Gui Inputs” finde, dass meinen aktuellen Ansatz sowas von in den Schatten stellt, werde ich es auch beibehallten ;).