2
votes

Utiliser le sous-type d'une classe dans une classe générique

J'essaie d'utiliser le type générique d'une classe dans une autre classe qui utilise celle-ci:

Kotlin

class StringCallback: Callback<String>() { ... }

fun example(callbackParam: Controller<StringCallback>) { ... }

Java

class Controller <E extends Callback<O>, O> { ... }

Une solution simple est de déclarer les deux types E et O sur la classe Controller de cette manière:

Kotlin

class Controller <E, O> (callback: E) where E: Callback<O> { ... }

Java

class Callback <O> {}

class Controller <E extends Callback<O>> {
    Controller(E callback) {}
    public void foo1(O a) {...}
    public O foo2() {...}
}

Cependant, je veux éviter de déclarer les deux types sur le constructeur car l'information est répétée et ce n'est pas si joli.

Modifier

J'ai besoin du type E sur le constructeur car J'ai besoin de définir des fonctions telles que:

class Callback <O> () 

class Controller <E> (callback: E) where E: Callback<O> {
    fun foo1 (a: O) {...}
    fun foo2 (): O {...}
}

Je veux que l'objet callbackParam utilise mon StringCallback défini, pas n'importe quel Callback .

Et rappelez-vous que j'ai également besoin du type O dans la classe Controller pour fonctionner avec ce type . Puis-je "l'extraire" ou "l'inférer" sans le déclarer dans le constructeur générique?

[@LppEdd avec votre permission, j'utiliserai votre classe :)]

Une idée?

Merci d'avance !!


8 commentaires

Pourriez-vous publier un cours complet? Cela n'a pas besoin d'être correct, je veux juste voir votre "vision"


Je veux dire, la fonction que vous avez définie ici est parfaitement correcte. Il n'acceptera que StringCallback, dans ce cas.


Oui, mais le problème est que si je fais comme ça, dans la classe Controller je ne peux pas utiliser le type d'objet O dont je sais que c'est une chaîne. Comprenez-vous le problème ou avez-vous besoin de plus d'informations?


Donc, fondamentalement, vous avez besoin d'accéder au type générique de rappel, non? Vous devrez peut-être avoir une méthode dont le type de retour est égal à ce type générique, non?


J'ai peut-être mal compris, mais si tel est le cas, vous y avez déjà accès. Je veux dire, regardez les deux méthodes (foo1 et foo2) de la classe Controller que j'ai postée, elles ont accès à E ( que vous pouvez également appeler O ), qui est String.


Donc, fondamentalement, vous avez besoin d'accéder au type générique de rappel, non? oui Vous devrez peut-être avoir une méthode dont le type de retour est égal à ce type générique, non? oui


Je vois votre exemple et c'est une bonne idée, mais cela ne me sert pas, car ce dont j'ai besoin, c'est d'une fonction comme fun example () qui nécessite un Controller en tant que paramètre, pas un Controller . J'en ai besoin car je veux accepter uniquement mon StringCallback , pas n'importe quel Callback .


Cette fonction serait-elle dans la classe Controller, ou où? Quoi qu'il en soit, je pense qu'un talon complet du résultat souhaité est meilleur. Alors je peux mieux te répondre


3 Réponses :


2
votes

Ce que vous pouvez faire est

class StringCallback extends Callback<String> { ... }
class IntegerCallback extends Callback<Integer> { ... }

final Controller<String> c1 = new Controller<>(new StringCallback());
final Controller<Integer> c2 = new Controller<>(new IntegerCallback());

Exemple:

Controller(final Callback<? extends E> callback)

C'est parce que vous savez déjà que vous accepterez un rappel > .
Il n'est pas nécessaire d'utiliser E extend Callback . Mais continuez à lire

Ce

final Controller<String> stringController = new Controller<>(new Callback<>());

signifie que le constructeur acceptera chaque type qui est un sous-type de Callback

class Controller<E> {
    private final Callback<E> callback;

    Controller(final Callback<E> callback) {
        this.callback = callback;
    }

    public void foo1(final E a) { callback.set(a); }
    public E foo2() { return callback.get(); }
}


1 commentaires

Merci pour la réponse LppEdd! Cependant, j'ai besoin que le type générique soit dans le constructeur. J'ai édité la question pour être plus claire



1
votes

Vous pouvez raccourcir le code:

class Controller<E : Callback<O>, O> (callback: E) { .. }

Mais cela ne vous laissera pas une chance de vous débarrasser du deuxième paramètre générique. Vous utilisez le type O dans les fonctions de votre classe.

Vous pouvez essayer de dire class Controller > ou class Controller > si vous le faites pas besoin du type O réel dans le code de la classe Controller .

L'utilisation de Callback peut nécessiter de déclarer les paramètres de type Callback avec variance. Dans Kotlin, on définit la variance au niveau de la déclaration. Voir https://kotlinlang.org/docs/reference/generics.html# déclaration-site-variance
pour plus de détails.


0 commentaires

2
votes

Vous n'avez pas besoin de connaître le type du Callback juste que son paramètre générique est E .

Vous pouvez donc déclarer votre classe dans Kotlin comme ceci :

public class Controller<O> {
    public Controller(Callback<O> callback) { ... }
}

Et en java comme ceci:

class Controller<O>(callback: Callback<O>) { ... }

Déclarer le type du Callback être générique n'aurait de sens que si vous permettiez à l'appelant de votre contrôleur d'interagir explicitement avec le rappel. Mais à partir du code que vous avez donné, vous ne faites que renvoyer ou accepter des valeurs de / vers le rappel.


2 commentaires

Je renommerais E en O comme dans la question.


Merci pour la réponse Lino! Cependant, j'ai besoin que le type générique soit dans le constructeur. J'ai édité la question pour être plus claire