Наследование и кастинг: это хорошая Java? - PullRequest
5 голосов
/ 03 февраля 2012

Посмотрите на этот код (от здесь )

abstract class EntityA {
    AssocA myA;
    abstract void meet();
}

abstract class AssocA {
    int something;
    abstract void greet();
}

class AssocAConcrete extends AssocA {
    void greet() {
        System.out.println("hello");
    }
    void salute() {
        System.out.println("I am saluting.")
    }
}

class EntityAConcrete extends EntityA {
    void meet() {
        System.out.println("I am about to meet someone");
        ((AssocAConcrete)myA).salute();
    }
}

Существует два параллельных дерева наследования для родительского класса и связанного класса. Проблема со строкой 23:

((AssocAConcrete)myA).salute();

Это боль, и у меня есть такие вещи по всему моему коду. Хотя эта строка является частью конкретной реализации Entity, я должна напомнить, что я хочу использовать конкретную реализацию AssocA, AssocAConcrete.

Есть ли какая-то аннотация, чтобы объявить об этих отношениях? Или есть лучший, более разговорный Java-способ выразить этот дизайн? Спасибо!


Это в ответ на @Dave, потому что я хочу поместить некоторый код в ...

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

AssocAConcrete myAssoc = new Assoca();
EnitityA<T extends AssocA> myEntity = new EntityA<AssocAConcrete>();
myEntity.setAssoc(myAssoc);
myAssoc.salute();

Да? Это действительно круто. Я думаю, что буду использовать это!

Ответы [ 4 ]

8 голосов
/ 03 февраля 2012

Я думаю, что это намного удобнее, если использовать дженерики ...

abstract class EntityA<T extends AssocA> {

    // Basically, this means myA is at least an AssocA but possibly more...
    T myA;
    abstract void meet();
}

abstract class AssocA {
    int something;
    abstract void greet();
}

class AssocAConcrete extends AssocA {
    void greet() {
        System.out.println("hello");
    }
    void salute() {
        System.out.println("I am saluting.");
    }
}

class EntityAConcrete extends EntityA<AssocAConcrete> {
    void meet() {
        System.out.println("I am about to meet someone");
        myA.salute();
    }
}

Помимо того, что вы избегаете приведения типов, это также значительно упрощает добавление различных функций в ваши AssocA реализации.Всегда должен быть способ делать вещи без использования фиктивных реализаций (то есть методов, которые просто генерируют исключение NotImplementedException) или приведения.Хотя это не всегда легко и не стоит потратить время на рефакторинг.Другими словами, никто не будет обвинять вас в кастинге (ну ... может быть, некоторые люди будут, но вы не сможете угодить всем).

РЕДАКТИРОВАТЬ (Примечания по созданию экземпляров):

Из комментариев @pitosalas ниже ...

//Won't work...can't call 'new' on abstract class AssocA
AssocAConcrete myAssoc = new Assoca();

//Instead, do this...
AssocAConcrete myAssoc = new AssocAConcrete();

А потом ....

// Again, won't work.  T is only declaring the type inside your class/method.
// When using it to declare a variable, you have to say EXACTLY what you're making,
// or at least something as exact as the methods you're trying to invoke
EnitityA<T extends AssocA> myEntity = new EntityA<AssocAConcrete>();

//Instead do this...
EnitityA<AssocAConcrete> myEntity = new EntityAConcrete();

// Or this...
EntityAConcrete myEntity = new EntityAConcrete();

И тогда это должно быть хорошо ...

// Assuming this is defined as `public void setAssoc(T newAssoc) {this.myA = newAssoc;}`
myEntity.setAssoc(myAssoc);
myAssoc.salute();
3 голосов
/ 03 февраля 2012

выглядит подозрительно для меня. В приведении нет ничего страшного, но в этом случае вы можете решить эту проблему, введя метод salute в AssocA. Подклассы AssocA могут предоставлять свои реализации; это часть выгоды от наследования.

То, что вы сейчас делаете, говорит, что все экземпляры EntityA имеют экземпляр AssocA, но затем в вашем методе meet вы в основном заставляете экземпляр AssocA быть экземпляром AssocAConcrete. Это та часть, которая подозрительна; почему AssocA существует, если вам действительно нужен AssocAConcrete.

Другой вариант (на основе ваших комментариев) - вызвать salute в методе greet. Таким образом, определенный подкласс имеет определенное поведение greet, определенное в суперклассе, и делает то, что хочет. В этом случае salute может стать частным или защищенным. Другая реализация может легко сделать что-то другое, например runLikeHell.

0 голосов
/ 03 февраля 2012

Если у вас есть большая часть кода, где вы используете ссылку «myA», вы можете объявить другую ссылку следующим образом:

public AssocAConcrete myAConcrete = (AssocAConcrete)myA;

теперь вы можете использовать новую ссылку myAConcrete и получить доступ к функциям AssocAConcrete класса.

Если вам нужно сделать это во многом так, как упоминалось в hvgotcodes, вам, вероятно, следует рассмотреть возможность перехода метода к классу AssocA.

0 голосов
/ 03 февраля 2012

Проблема параллельных иерархий классов очень реальна и действительно отстой. Логическая связь, в которой AssocAConcrete всегда идет с EntityAConcrete, не может быть выражена системой типов.

Вы не можете специализировать тип myA в EntityAConcrete как AssocAConcrete, не скрывая его от суперкласса. Я думаю, что самая близкая работа, которая обращалась к этому, была " Семейный полиморфизм ", но это не основное направление.

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