Generics

Habe ein kleines Problem mit Generics:


	String foo();
}

public enum SomeEnum implements SomeInterface {

	FOO("foo");

	private String foo;

	public String foo() {
		return foo;
	}
}

public abstract class SomeClass {

	protected abstract <T extends Enum<T> & SomeInterface> Class<T> getEnumClassType();
}

public class SomeClassImpl {

	@Override
	protected abstract <T extends Enum<T> & SomeInterface> Class<T> getEnumClassType() {
		/* Class<SomeEnum> cannot be converted to Class<T> */
		return SomeEnum.class;
	}
}```

Obwohl ich T als Unterklasse von Enum definiere und dass dieses dann SomeInterface implementiert, kann ich den Quellcode so nicht nutzen, da ich immer eine Fehlermeldung erhalte. Ich verstehe aber nicht warum.
Ich brauche das um zur Laufzeit die Klasse des Enums zu erhalten und so dann über 

Enum.valueOf(Class cls, String name).foo()

 die Werte zu erhalten. Da ich mehrere Klassen habe mit unterschiedlichen enums ist das leider nicht ganz zu vermeiden. Ich kenne die Klassen nicht im voraus und kann daher nicht feste Enums verwenden. Wenn jemand diesen generischen Teil zum Laufen bringen würde... :)

Die Fehlermeldung wäre ganz interessant. Auf den ersten schnellen Blick würde ich sagen, dass SomeEnum der Konstruktor fehlt, den du in Line 8 aufrufst.

Du kannst einfach

public class SomeClassImpl extends SomeClass {

    @Override
    protected Class<SomeEnum> getEnumClassType() {
        return SomeEnum.class;
    }
    
}

schreiben. Deine Variante kann nicht funktionieren, auch wenn es schwer zu erklären ist: SomeEnum ist nur eine aller möglichen Klassen, die die Bedingungen erfüllt, es ist nicht “genau die” Klasse mit T extends Enum<T> & SomeInterface, davon kann es tausende geben. Eine konkrete Klasse kann diese Bedingungen also gar nicht erfüllen. Durch die Spezialisierung des Rückgabewertes (Stichwort “Kovarianz”) kommst du um dieses Problem herum, weil du dann nicht weniger als “versprochen” lieferst. Trotzdem erfüllst du damit den Kontrakt der abstrakten Klassen, das @Override funktioniert also.

Ich verstehe den Ansatz nicht. Wass genau willst Du denn erreichen?

Wieso musst Du zur Leufzeit eine Konstante aus einem unbekannten enum ermitteln?

Wäre es nicht sinnvoller alle enum-Werte in eine Map<String,SomeInterface> zu werfen und von dort raus zu holen?

bye
TT

Jedes Enum hält ca. 700 Werte, das ‘foo’ ist dabei nur ein einziges char… Daher weiß ich nicht ob da eine Map sinnvoller wäre.

EDIT:
ich habe mal noch etwas rumgespielt und bin dabei auf was gestoßen, was ich nicht ganz verstehe…

public abstract class SomeClass<T extends Enum<T> & SomeInterface> {


    public SomeClass(Class<T> cls) {
        // in variable ablegen...
    }

    /* weitere abstrakte Methoden.... */
}

Diese Lösung funktioniert. Jede Subklasse ruft den Konstruktor auf und übergibt dann die Klasse (im Beispiel war das ‘SomeEnum.class’). Könnte mir jemand vielleicht den Unterschied zu meinem ersten Ansatz erklären?

es kann generell nicht ein Objekt auf einen beliebig definierten Typ T umgewandelt werden,
das hat nichts mit deinem Interface zu tun (hättest ohne testen können) und auch nichts mit Enum

    {
        return "";
    }

kann auch nicht String als T annehmen,

solche Methoden kann man nicht direkt generisch schreiben,
hier ein (T) davor (bzw. bei dir (Class) oder auch nur (Class) ) + Warnung ignorieren geht aber


warum, das frag mich lieber nicht genau :wink: , hier habe ich aber doch eine gewisse Idee:
mit so pauschalen T draußen dran könnte jemand von außen, der nur das T sieht,
genausogut

    Class<SomeEnum2> x = impl.getEnumClassType();
``` aufrufen mit SomeEnum2 statt SomeEnum, 

es gibt keine Info welcher Enum-Typ es ist, und diese Art Methoden erlauben genauen Ziel-Typ beim Aufrufer,

wahrscheinlich interessiert du nicht für den konkreten Typ, 
noch die Frage ob und wie man das ausdrücken könnte, mit `? extends` usw...



bei der Konstruktor-Variante kommt die Bindung erst durch/ beim Aufruf, daher dort einfacher zu machen

[quote=PositiveDown]Jedes Enum hält ca. 700 Werte[/quote]Es gibt mehrere enums, die SomeInterface implementieren und in jedem gibt es 700 Enum-Konstanten mit jeweils eigenen Implemetierungen für die Methoden von SomeInterface?
Da würde mich wirklich mal interessieren, was das wird, ein Spiel?

[quote=PositiveDown;127681]Könnte mir jemand vielleicht den Unterschied zu meinem ersten Ansatz erklären?[/quote]Das ist quasi die Umkehrung von @Landei ****s Erklärung: auch hier trifft die Generics-Kombination auf viele mögliche konkrete Klassen zu, das übergebene Objekt kann aber nur einer angehören…

bye
TT

selbst fürs gepostete Beispiel SomeEnum hat doch nicht jede Konstante (wie gepostet FOO) eine eigene Implementierung…,
die Handvoll Enum-Klassen haben die Simple-Implementierung, jeweils die interne foo-Konstante jeder Enum-Konstante zurückzugeben,

immer noch viel, aber es klingt jedenfalls sinnvoll, diese Werte in Enums zu haben statt in Maps,
warst du nicht in früheren Themen ein strenger Verfechter ‚alles in Enums‘, keine switche usw.? :wink:

das Interface ist nur da, um mehrere Enums gemeinsam ansprechen zu können,
dann kann auch leicht dynamisch eine neue aufgeräumte Enum dazukommen (Plugin)

Nope, Bindings für Fonts, genauer gesagt Icon Fonts. Ich habe eins vor einigen Monaten erstellt und wollte das nun in JavaFX machen. Jedes Bild repräsentiert einen Buchstaben im Font (z.b. 0xf101). Dazu habe ich ein Enum erstellt das als Attribut ein char besitzt und dann jedes Icon als Enum-Wert hinterlegt (z.B. CAR(‚\uf101‘). Hätte ich nur einen Font wäre das ganze ziemlich easy. Aber ich habe genau genommen drei verschiedene. Deshalb wollte ich den gleichen Quelltext auslagern und dann für jeden Font eine spezifische Implementierung vornehmen. Das Interface definiert nur eine Methode,

char getUnicodeCharacter()

(interface FontIcon).
Meine JavaFX Komponente, die Text erweitert, besitzt ein neues Css Attribute namens „-icon-name“, damit lege ich den Namen des Icons fest, das benutzt werden soll. Dieser Name entspricht dem Namen eines Feldes im Enum (wäre in dem Fall zum Beispiel ‚CAR‘). Deshalb brauche ich die Klasse des Enums zur Laufzeit. Ich würde dann das Feld über

FontIcon icon = Enum.valueOf(Class<T extends Enum<T>>, String name)

erhalten und da ich ja über die Generics festgelegt habe, dass es sich um eine Implementierung des Interfaces FontIcon handelt, kann ich es eben so schreiben.
Klar ich könnte es auch einfach nach FontIcon casten, aber ich hasse es, casts zu nutzen. Ich finde es persönlich irgendwie unsauber wenn es sich für relativ wenig aufwand vermeiden lässt und man dadurch dann auch noch Typensicherheit hat. Daher die Idee mit der abstrakten Methode. Die Methode gehört zu der Klasse, die Text erweitert. Für jeden Font wird diese Klasse erweitert. Sie lädt den Font, setzt ihn für die Textkomponente und besitzt noch ein paar andere Methoden und die dazu gehörigen css Eigenschaften.

Ich hoffe mal ihr habt das verstanden :slight_smile: wenn nicht, fragt nach :smiley:

Ich habe es mal mit

protected abstract Class<? extends Enum<?> & FontIcon> getEnumTypeClass()

versucht, jedoch scheint ‚&‘ ein illegaler Ausdruck in Kombination mit ‚?‘ zu sein. Rein theoretisch sollte dieser Ausdruck aber funktionieren. Denn ich muss nur festgelegt haben, dass es FontIcon implementiert und ein Enum ist (da ich

Enum.valueOf()

aufrufe…

Ja

…aber was kümmert mich mein Geschwätz von gestern…? :smiley:

IMHO sind enumns nur dann angesagt, wenn es für jede enum-Konstante auch sinnvolle selbstständige Implementierungen gibt. Wenn das Enum nur als Entscheidungskriterium in einem if/switch dient ist es Perlen vor den Säuen…

Wenn ich den OP richtig verstehe dienen die Enums zum übersetzen eines CodePoints in ein anzuzeigendes Bild. Wenn dass nicht fir Typische Aufgabe eines Mappings ist, dann weis ich auch nicht…

[quote=SlaterB;127687]das Interface ist nur da, um mehrere Enums gemeinsam ansprechen zu können,
dann kann auch leicht dynamisch eine neue aufgeräumte Enum dazukommen (Plugin) [/quote]Gerade dann sollte doch der Benutzer des Emum/Interfaces garnichts über das konkrete Enum wissen müsssen…
Ich sag mal su dem Bauch raus, dass da die Verteilung der Aufgaben noch nicht ganz sauber ist…

bye
TT

[quote=Timothy_Truckle;127694]Gerade dann sollte doch der Benutzer des Emum/Interfaces garnichts über das konkrete Enum wissen müsssen…
Ich sag mal su dem Bauch raus, dass da die Verteilung der Aufgaben noch nicht ganz sauber ist…[/quote]
Das heißt du würdest dann einfach eine Map<String, Character> nehmen?

[quote=PositiveDown]Das heißt du würdest dann einfach eine Map<String, Character> nehmen?[/quote]Ich kennen natürlich viel zu wenig von Deinem System nd da tatsächlich hilfreiche Anmerkungen zu Detailfragen zu machen, aber das sieht irgendwie blöd aus. Das wäre ja eine zusätzliche Indirektion.

Wenn die Strings die CodePoint-Definitionen enthalten wäre
Map<String, FondImage> naheliegender.

bye
TT

Ich habe es mal ohne Enum, nur mit Interface probiert:

public interface SomeInterface
{
	String foo();
}
public enum SomeEnum implements SomeInterface
{
	FOO("foo");

	private String foo;

	SomeEnum(final String str)
	{
		this.foo=str;
	}
	
	public String foo()
	{
		return foo;
	}
}
public abstract class SomeClass {
	 
    protected abstract <T extends /*Enum<T> &*/ SomeInterface> Class<T> getEnumClassType();
}```

public class SomeInterfaceImpl implements SomeInterface
{
@Override
public String foo()
{
throw new RuntimeException(“Auto-generated method stub”);
}
}


public class SomeClassImpl extends SomeClass
{
@Override
protected <T extends /Enum &/ SomeInterface> Class getEnumClassType()
{
/* Class cannot be converted to Class */
//return SomeEnum.class;
/Type mismatch: cannot convert from Class to Class/
return SomeInterfaceImpl.class;
}
}


Ich bekomme eine ähnliche Fehlermeldung.
Also liegt es nicht am Enum, sondern daran, dass der Compiler den Type-Parameter T nicht mit einem konkreten Type-Argument konkret machen kann.

Bezogen auf Dein Posting vom 28.12.2015 12:04 bedeutet dies, dass in den konkreten Unterklassen (von denen Du dort kein Beispiel aufgeführt hast, der Typ-Parameter T mit einer konkreten Klasse (Typ-Argument) konkret gemacht wird.