web-dev-qa-db-fra.com

Héritage de méthode générique Java et règles de substitution

J'ai une classe abstraite qui a une méthode générique et je veux remplacer la méthode générique en substituant des types spécifiques pour le paramètre générique. Donc, en pseudo-code, j'ai les éléments suivants:

public abstract class GetAndParse {
  public SomeClass var;

  public abstract <T extends AnotherClass> void getAndParse(T... args);
}

public class Implementor extends GetAndParse {
  // some field declarations

  // some method declarations

  @Override
  public <SpecificClass> void getAndParse(SpecificClass... args) {
    // method body making use of args
  }
}

Mais pour une raison quelconque, je ne suis pas autorisé à faire cela? Est-ce que je fais une sorte d'erreur de syntaxe ou est-ce que ce genre d'héritage et de substitution est interdit? Plus précisément, je reçois une erreur à propos de @Override car Eclipse IDE ne cesse de me rappeler d'implémenter getAndParse.

Voici comment je veux que le code ci-dessus fonctionne. Quelque part dans mon code, une méthode attend des instances d'objets implémentant GetAndParse, ce qui signifie spécifiquement qu'elles disposent d'une méthode getAndParse que je peux utiliser. Lorsque j'appelle getAndParse sur cette instance, le compilateur vérifie si j'ai utilisé les instances spécifiques de T de manière appropriée. Ainsi, en particulier, T devrait étendre AnotherClass et être SpecificClass.

28
davidk01

Nous avons ici deux méthodes différentes avec des paramètres de type individuels.

public abstract <T extends AnotherClass> void getAndParse(Args... args);

Il s'agit d'une méthode avec un paramètre de type nommé T, et délimité par AnotherClass, ce qui signifie que chaque sous-type de AnotherClass est autorisé en tant que paramètre de type.

public <SpecificClass> void getAndParse(Args... args)

Il s'agit d'une méthode avec un paramètre de type nommé SpecificClass, délimité par Object (ce qui signifie que chaque type est autorisé en tant que paramètre de type). Tu veux vraiment ça?

Le paramètre type est-il utilisé dans Args? Je pense que le problème serait là.


Le sens de 

public abstract <T extends AnotherClass> void getAndParse(T... args);

est que le appelant de la méthode peut décider avec quel paramètre de type il veut appeler la méthode, tant qu'il s'agit d'un sous-type de AnotherClass. Cela signifie qu’en réalité la méthode peut être appelée avec n’importe quel objet de type AnotherClass.

Etant donné que l'appelant peut décider du paramètre type, vous ne pouvez pas, dans une sous-classe, limiter le type du paramètre à SpecificClass. Il ne s'agirait pas d'une implémentation de la méthode, mais d'une autre méthode portant le même nom (surcharge).

Peut-être que vous voulez quelque chose comme ça:

public abstract class GetAndParse<T extends AnotherClass> {
  public SomeClass var;

  public abstract void getAndParse(T... args);
}

public class Implementor extends GetAndParse<SpecificClass> {
  // some field declarations

  // some method declarations

  @Override
  public void getAndParse(SpecificClass... args) {
    // method body making use of args
  }
}

Maintenant, la méthode getAndParse implémente la méthode de la classe parent.

28
Paŭlo Ebermann

Vous voyez ce problème à cause du concept appelé "Effacement" dans Java Generics . Java utilise "effacement" pour prendre en charge la compatibilité descendante. C'est-à-dire du code Java qui n'utilisait pas de génériques.

Procédure d'effacement:
Le compilateur fera d’abord une vérification de type, puis supprimera (effacera) tous les paramètres de type autant que possible, et insérera également TypeCasting si nécessaire.

exemple:

public abstract <T extends AnotherClass> void getAndParse(T paramAnotherClass);

va devenir

public abstract void getAndParse(AnotherClass paramAnotherClass);

En classe "Implementor.Java", 

Le code

public <SpecificClass> void getAndParse(T paramAnotherClass)

va devenir

public void getAndParse(SpecificClass paramAnotherClass){  }

le compilateur verra que vous n'avez pas implémenté correctement la méthode abstraite . Il existe une incompatibilité de type entre la méthode abstraite et la méthode implémentée. C'est pourquoi vous voyez l'erreur.

Plus de détails peuvent être trouvés ici . http://today.Java.net/pub/a/today/2003/12/02/explorations.html

10
kensen john

Non, ce n'est pas valide. Que se passerait-il si une personne avec une référence GetAndParse l'appelait avec un différent classe prolongeant AnotherClass

1
Erik

Cela devient un non-sens quand quelqu'un a une référence pour taper GetAndParse et tente d'appeler la méthode getAndParse. Si chat et chien étendent une autre classe. Je devrais m'attendre à pouvoir appeler GetAndParse # getAndParse avec un chat ou un chien. Mais la mise en œuvre a tenté de le restreindre et de le rendre moins compatible!

1
Affe

Vous ne pouvez pas override sur un type spécifique T car il n'y a en fait (au niveau du bytecode si vous le souhaitez) qu'une seule méthode getAndParse en raison du type effacement (voir autre réponse):

public abstract void getAndParse(AnotherClass... args); // (1)

Pour chaque type de T, la même méthode est utilisée.

Vous pouvez surcharger le (je pense):

public void getAndParse(SpecificClass... args); // (2)

mais ce ne sera pas une méthode différente de (1) et il sera appelé pas par un code générique:

T x = whatever;
object.getAndParse(x); // Calls (1) even if T is derived from SpecificClass
1
ysdx

La méthode statique ne peut pas remplacer

class Vehicle{
static void park(int location){
    System.out.println("Vehicle parking..");
}}

class Car extends  Vehicle{
@Override //error
void park(int location) { //error
    System.out.println("Car Parking..");
}}

La méthode privée ne peut pas remplacer

class Vehicle{
private void park(int location){
    System.out.println("Vehicle parking..");
}
void callPark(){
    park(100);
}}

class Car extends  Vehicle{
//@Override
void park(int location) {
    System.out.println("Car Parking..");
}}

class Demo {
public static void main(String[] args) {
    Vehicle v1=new Car();
   v1.callPark();
}}

La méthode finale ne peut pas remplacer

class Vehicle{
final void park(int location){
    System.out.println("Vehicle parking..");
}}

class Car extends  Vehicle{
//@Override
void park(int location) { //error
    System.out.println("Car Parking..");
}}
0
Ishan Lakshitha