наследование Java - PullRequest
       4

наследование Java

5 голосов
/ 24 марта 2011

Учитывая эти классы:

class Father {
  public Father getMe() {
    return this;
  }
}

class Child extends Father { .. }

Я вызываю открытый метод класса Father из класса Child:

Child c = new Child();
Child c1 = c.getMe();

Итак, это не работает, у меня естьиспользовать приведение, чтобы заставить это работать:

Child c1 = (Child) c.getMe();

Вопрос: есть ли лучший способ заставить это работать без броска?может быть так ??:

public <T extends Father> T getMe() {
  return this;
}

Заранее спасибо.

Ответы [ 5 ]

4 голосов
/ 24 марта 2011

Вы должны переопределить метод getMe с типом возврата Child в классе Child. Это будущее называется ковариантным возвратом, оно поставляется с jdk1.5 ..

class Child extends Father{  
       public Child getMe() {    
             return this;  
        }
   }

А как насчет этого решения? Это не выглядело элегантно, вы должны привести к T в методе getThis. Но нет необходимости переопределять метод getThis для подклассов. Но, к сожалению, безопасность типов не применяется, такой базовый объект можно определить, Base<Child> baseChild = new Base<Child>(); Поэтому я не рекомендую этот метод ..

class Base<T extends Base<T>> {
    public  T getThis() {

        return  (T) this;
    }
}

class Child extends Base<Child>{

}

Child child = new Child();
child.getThis(); // return type is Child
Base base = new Base();
base.getThis(); // return type is base
3 голосов
/ 24 марта 2011

Это работает :)))

Child c = new Child();
Child c2 = c; //same as c.getMe()
2 голосов
/ 24 марта 2011

Нет, на самом деле нет, потому что Child является подклассом Father, а не наоборот.Если бы у вас было

public Child getMe();

Вы могли бы сделать

Father father = c.getMe();

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

В конце концов, в концедень, как сказал один из комментаторов, если вам действительно нужно точно знать, к какому типу относится экземпляр, возможно, вы делаете что-то не так.Реальное преимущество наследования - это возможность отделить части вашего кода.Код клиента не заботится о том, какова его реализация, он просто знает, какие методы нужно вызвать, чтобы что-то сделать.Конечно, есть некоторые случаи, когда вам нужно привести объект в действие, потому что вам нужно точно знать, какой это тип, но это должно быть редким случаем.В этом случае значение будет определять метод, который принимает объект Father в качестве параметра и понимает, как работать с интерфейсом Father.Все необходимые ему методы будут определены как часть класса Father.Другой код может создавать пользовательские подклассы Father, которые позволят им расширить функциональные возможности класса, в то же время позволяя им работать с кодом, разработанным для интерфейса Father.

1 голос
/ 24 марта 2011

Канонический способ сделать это - использовать любопытно повторяющийся шаблон :

class Father<T extends Father> {
  T getMe() { return (T)this; }
}

class Child extends Father<Child> {}

Child child = new Child().getMe(); // OK now.

Это выполняет то, что вы хотите. Есть несколько необычных, но допустимых случаев использования, когда базовому классу нужен доступ к его производному типу. Одним из примеров является что-то вроде этого:

class Cached<T extends Cached> {
  public static T create(String name) {
    T cached = sCache.get(name);
    if (cached != null) return cached;

    cached = // create new T...
    sCache.put(name, cached);
    return cached;
  }

  private static Map<String, T> sCache = new HashMap<String, T>();
}

Это хитрый трюк, но он сопряжен со стоимостью: вы нарушили отношение подтипа. Ваш Father класс больше не является суперклассом Child, поскольку не простой Father класс, просто Father<T>. Возможно, вам удастся сделать несколько хитрых подстановочных знаков, чтобы получить подтип обратно, когда вам это нужно, но вы найдете это громоздким. В зависимости от вашей проблемы это может быть полезным компромиссом.

0 голосов
/ 24 марта 2011

Java плохо поддерживает ковариацию.

Это самое близкое к чему-то подобное: (но это не практично)

public abstract class AFather<P extends AFather<P>> {
    public P getMe() { return (P)this; }
}
public class Father extends AFather<Father> {}
public abstract class AChild<P extends AChild<P>> extends AFather<P> {}
public class Child extends AChild<Child> {}

Наиболее практичный способ сделать это - переопределить метод - это подкласс:

public class Father {
    public Father getMe() { return this; }
}
public class Child extends Father {
    public Child getMe() { return (Child)super.getMe(); }
}

Кстати, я не очень хорошо знаю Scala, но этот язык обрабатывает хорошие ковариантные и контравариантные типы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...