Generics machen mich verrückt

Hag2bard

Bekanntes Mitglied
Hallo,

ich verstehe den merkwürdigen Aufbau von Generics nicht. Umso mehr ich google umso schlimmer und unverständlicher wird es.
Ich habe folgendes Szenario:

Java:
private static String getResponse(final String decision)
{
    switch(decision)
    {
            case "1" ->
            {
                return new String("string");
            }
            case "2" ->
            {
                return new String("3");
            }
    }
}

Ich möchte nun aber, dass statt Strings verschiedene Klassen zurück gegeben werden.
Mein IntelliJ meckert aber nun bei den returns rum, da er das ganze zu T casten möchte.

Mein Code sieht so aus:
Java:
private static <T> T getResponse(final String decision)
{
    return new String("test");
}

Die Meldung die IntelliJ bringt ist folgende:

1692970726610.png
1692970748429.png

Was ist da los?
 
Das ist doch verständlich. Du schreibst eine Methode getResponse, die ein T zurück geben soll. T kann kann ja alles sein. Also z.B. kann ich die generische Methode ja nutzen, um ein Integer zurück zu bekommen. Oder einen Kunden. Oder was auch immer.

Aber die Implementierung mit return new String("test") gibt immer ein String zurück.

Was T sein soll, wird ja später erst festgelegt an der Stelle, an der deine Generische Klasse / Methode genutzt wird.
 
Du kannst in einem Generic nur eine Klasse deklarieren.

Was dank Vererbung aber geht: Wenn du verschiedene abgeleitete Klassen hast kannst du die Superklasse deklarieren und alle abgeleiteten Klassen gehen da rein.

Du kannst z.B. eine Liste für die Klasse 'Tier' deklarieren und darin 'Hund-', 'Katze-', und 'Ente'-Objekte, die alle von 'Tier' erben, reinwerfen. Ein Hund ist ein Tier, also geht das.

Und absolut alles in Jave erbt von 'Objekt', aber wenn du Generics von Object deklarieren willst, wird wahrscheinlich mit deinem Design so einiges nicht hinhauen.
 
Falls Du eine Methode haben willst, dessen Rückgabe ganz dynamisch ist, dann kannst Du das leider nicht ganz so trivial bauen.

Das Problem ist, dass durch type erasure der konkrete Typ Parameter verschwindet und zur Laufzeit diese Information nicht mehr da ist. Daher muss man den Typ selbst noch irgendwie bekommen und dazu kann man die Klasse von T als Parameter anfordern, also ein Parameter vom Typ Class<T>

Das kann dann z.B. so aussehen (Ich baue einfach einmal ein sinnloses Beispiel für einen universellen Konverter, der dann mit einer Methode aufgerufen werden kann).

Java:
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class Converter {

private Map<Class, Function<String, Object>> converter = new HashMap<>();

    public Converter() {
converter.put(Integer.class, (String s) -> Integer.parseInt(s));
    }

public  <T> T getValue(Class<T> resultClass, String value) {
Function<String, Object> converterMethod = converter.get(resultClass);
        if (converterMethod == null) throw new IllegalStateException("No converter available for class " + resultClass.getName());

        return (T) converterMethod.apply(value);
    }
}

Nutzbar wäre es dann z.B. so:
Java:
public class Test {
public static void main(String[] args) {
Converter converter = new Converter();

        Integer i = converter.getValue(Integer.class, "3");
        System.out.println(i);
    }
}

Das Beispiel zeigt, wie etwas aussehen kann, wenn man in einer generischen Klasse weiterhin den genauen Typ Parameter benötigt.
Ich habe nur eine konkrete Methode hinterlegt (String -> Integer), aber das kann natürlich noch beliebig erweitert werden.

Sowas braucht man aber eher selten.

Man kann da z.B. ein Szenario bedenken: ResultSet einer JDBC Query. Da kann man dann ja diverse Felder in diversen Typen aufrufen. Also könnte man da dann je nach Typ die entsprechende Methode des ResulSet aufrufen. Aber was gewinnt man dadurch?

Statt rs.getInt("MyField") hat man dann etwas wie get(Integer.class, "MyField") wobei get dann so eine Wrapper Methode wäre oder so.

Ich bin mir nicht sicher, ob oder wann man da etwas gewinnen würde. Evtl. wenn das, was da passieren soll, dynamisch ist?

Daher: Es geht hier nur um das Thema technische Machbarkeit. Es kann Fälle geben, da man sowas braucht und daher sollte man sowas durchaus kennen.
 
Ich habe die Methode umgebaut, so dass ich nur einen Rückgabetyp bestimmt habe(DTOs). Das ganze ist übrigens ein Miniserver der get httpRequests in Form von Json im http Body empfängt(z.B. ein Request nach einem Kunden), diese dann zu einem DTO verarbeitet. Eine Schnittstelle zwischen einer Planungssoftware und einer Einzelhandelssoftware die Kunden, Planungen, Kaufverträge usw hin und her schiebt. In dieser stark verkürzten und abgewandelten Methode, von der ich rede baue ich nun DTOs und liefer die zurück. Das sind DTOs wie Kunde, Adresse aber auch einen ErrorReport. Diese DTOs werden dann einfach zu JSON Strings umgewandelt und zurück geschickt.

Ist ein interessantes Projekt, hier habe ich auch viel über ExceptionHandling gelernt.
 
Du hast eine Restschnittstelle zwischen zwei Systemen implementiert.

Du kannst gerne deinen Quellcode veröffentlichen, dann können wir es für dich reviewn.
 
Das, was Du beschreibst, ist natürlich sehr oberflächig. Aber das ist genau das, was die Frameworks so machen. Egal ob Jakarta EE, Spring Boot, Quarkus, .... Du definierst die Schnittstelle mit den DTOs und das Framework kümmert sich dann darum. Die Umwandlung von JSON in ein DTO machst Du also nicht selbst.

Einfach mal schnell ein Beispiel per Google gesucht - ich habe da jetzt mal Spring Boot REST Services ausgesucht:
Getting Started | Building REST services with Spring
Da sieht man dann natürlich erst einiges Spring Boot typische mit der Entity und der Klasse mit der main Methode .... aber der Controller enthält dann einfach nur etwas wie:

Java:
  @PostMapping("/employees")
  Employee newEmployee(@RequestBody Employee newEmployee) {
    return repository.save(newEmployee);
  }

Sprich: Im Request wird dann ein Employee Entity erwartet. Das kann dann als JSON oder XML übergeben werden (Kann man natürlich konfigurieren wenn man möchte). Und die Entity, die zurück gegeben wird, wird auch automatisch serialisiert. Also je nach Anforderung vom Client kann das JSON oder XML sein (Wenn man alles zu lässt, dann kann der Client das in seinem Request in den Headern bestimmen).

Aber evtl. machst Du da ja auch irgend etwas anderes, wo diese Lösungen so nicht verwendet werden können oder ich habe Dich einfach falsch verstanden.
 
Genau es geht hier um Spring Boot und über die Lösung die gerade genannt hast, habe ich nun einen Tag mal nachgedacht. Ich nutze die Klasse HttpServer um die Requests zu empfangen. Gibt es da für Spring Boot eine gutes Cheatsheet? Ich möchte dass der Request ein Entity ist.
 
Poste mal deine HttpServer klasse.

Das Beispiel von Konrad zeigt ja, wie man entities verwendet.

Spring Boot ist ausgezeichnet dokumentiert. Da liefert Google alles zu.
 

Zurück
Oben