2016-03-12 3 views
0

Представьте У меня есть абстрактный класс Animal с методом:Абстрактные классы, наследующие абстрактные методы с различными тип параметра

public abstract void fetch(Animal ani); 

А потом у меня есть класс, который расширяет этот абстрактный класс со следующим:

public class GoldenRetriever extends Animal { 
     public void fetch(GoldenRetriever pup) { 
      pup.paws = "I have paws"; 
     } 
} 

Я хочу, чтобы каждый класс, который расширяет Animal, имеет метод fetch(). Однако метод выборки присваивает некоторую уникальную особенность данному животному (собачьи лапы, кошачьи когти и т. Д.). Например, выборка() для кота будет принимать параметр выборки (FelineCat котенок) и сказать:

public void fetch(FelineCat kitty) { kitty.claws = "I have claws."; } 

Таким образом, метод выборки принимает параметры, которые расширяют абстрактный класс Animal (и, таким образом, животные). Я определил метод fetch() в Animal, чтобы взять любой параметр типа Animal. Для меня это имеет смысл, так как кошки и собаки - животные, но Java говорит, что GoldenRetriever не может переопределить абстрактный метод fetch (Animal ani). Почему это?

+0

Что делать, если у вас есть 'Animal' класс с подпись, подобная этому методу 'Animal ' и 'Animal'' метод 'fetch' принимает параметр типа' T'. Тогда все ваши подклассы могут иметь метод «выборки» с их собственным типом. Так, например, 'public class FelineCat extends Animal ' class имеет подпись метода 'void fetch (FelineCat kitty)' like this – Bunti

+0

Не ваш метод выборки, просто используя сам объект? как в этом или в себе, поэтому нет необходимости в обработке параметров, метод является внутренним – Mixone

ответ

1

Не уверен, что, если я понял вашу проблему 100% правильно, но я думаю, что у меня есть какое-то решение для вашей проблемы. Следующий код работает, он может быть не оптимальным, поскольку вам всегда нужно использовать общий аргумент, но, по крайней мере, типизация верна.

public abstract class Animal<T extends Animal> { 

    public abstract void fetch(T ani); 
} 

public class GoldenRetriever extends Animal<GoldenRetriever> { 

    @Override 
    public void fetch(GoldenRetriever ani) { 

    } 
} 
+0

. Большой недостаток заключается в том, что вы больше не можете, например, написать такой метод, как 'public void someMethod (Animal x)', который может принимать любые ' Animal' как параметр. В зависимости от потребностей программы эта потеря ключевой функции OO может быть шоу-пробкой. Поэтому это не обязательно решение. – ajb

+0

Это просто неправильно, почему не следует ставить такой метод в класс Animal? –

+0

Я не говорю о методах, которые войдут в класс «Животное». Я говорю о методах, которые будут написаны в другом месте, которые будут работать с параметром «Animal». – ajb

0

Метод, который переопределяет другой метод, должен иметь одинаковые параметры, другие - это перегрузка. Ваш метод fetch должен быть объявлен как это:

public void fetch(Animal ani) 
0

Вы можете сделать свой Animal класс родовым, как этого

abstract class Animal<TAnimal extends Animal<?>> { 
    public abstract void fetch(TAnimal ani); 
} 

class GoldenRetriever extends Animal<GoldenRetriever> { 

    String paws; 

    @Override 
    public void fetch(GoldenRetriever pup) { 
     pup.paws = "I have paws"; 
    } 
} 

Это называется Curiously recurring template pattern

1

метод не переопределяет, так как тип параметра отличается. Вы должны объявить метод как это:

public class GoldenRetriever extends Animal { 
    public void fetch(Animal pup) { 

т.е. тип параметра еще должен быть Animal, даже если вы пишете класс, который расширяет Animal.

Для этого есть веская причина. Предположим, у вас есть какой-то другой метод

public void someMethod(Animal x) 

Вы можете пройти любой Animal в качестве параметра, в том числе GoldenRetriever:

GoldenRetriever gr = ...; 
someMethod(gr); 

Предположим, что в someMethod у вас это:

public void someMethod(Animal x) { 
    FelineCat kitty; 
    ... 
    x.fetch(FelineCat); 

С Animal метод fetch может принимать любые Animal, компилятор считает, что он должен быть OK, чтобы сделать этот вызов, потому что FelineCat - это Animal. Он не знает, что x на самом деле GoldenRetriever.Если бы это позволило переопределяющему методу принять параметр GoldenRetriever, возникла бы проблема, так как этот параметр фактически равен FelineCat, а не GoldenRetriever. Итак, когда fetch называется полиморфно, а тип параметра ошибочен, то что?

У компилятора нет возможности предотвратить это, поскольку он не знает, что такое тип x. Теоретически Java может разрешил переопределять изменение типа параметра и сделал проверку во время выполнения (возможно, выбрасывая ClassCastException, если он попытался получить GoldenRetriever, чтобы получить другой вид Animal). Я не знаю, почему они этого не сделали, но я уверен, что есть веские причины не делать этого таким образом.

Но вы можете сделать свой собственный контроль выполнения:

public class GoldenRetriever extends Animal { 
    public void fetch(Animal pup) { 
     if (pup instanceof GoldenRetriever) { 
      GoldenRetriever puppy = (GoldenRetriever)pup; 
      // now puppy is viewed as a GoldenRetriever, and any methods or 
      // instance variables particular to GoldenRetrievers can be accessed 
     } else { 
      throw new WrongSpeciesException("..."); // or whatever 
     } 
    } 
} 

UPDATE: выше ответ предполагает, что метод подписи действительно то, что вы хотите. Но, следуя комментарию Mixone, может быть, что вы - , а не, пытающийся настроить операцию, которая включает в себя двух животных, и в этом случае у вас не должно быть параметра вообще. Способ, как

public void fetch(GoldenRetriever pup) 

предполагает, что у вас есть один GoldenRetriever, что будет делать что-то с другой GoldenRetriever, и что вы просто не хотите GoldenRetriever делать это с другими видами животных. В этом случае я считаю, что проверка времени выполнения - лучший подход. Но если вы хотели операцию, которая работала только на одном животном, вы написали это неправильно.

0
/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package fetchanimal; 

/** 
* 
* @author Amin 
*/ 
public abstract class Animal <T>{ 


    public abstract void fetch(T animalType); 
} 

Создайте новый класс

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package fetchanimal; 

/** 
* 
* @author Amin 
*/ 
public class FelineCat extends Animal<FelineCat> { 
    public String claws ; 
    @Override 
    public void fetch(FelineCat animalType) { 
    // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 
     animalType.claws = "hello"; 

    } 

} 

и теперь создать третий класс

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package fetchanimal; 

/** 
* 
* @author Amin 
*/ 
public class GoldenRetriever extends Animal<GoldenRetriever>{ 
    public String paws ; 


    @Override 
    public void fetch(GoldenRetriever animalType) { 
     // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 
     animalType.paws = "hello" ; 
     } 


} 

просто скопировать вставить код

Смежные вопросы