Как мне расширить класс Java, который создается другим классом? - PullRequest
2 голосов
/ 30 апреля 2009

Учитывая два Java-класса, A и B, где A обычно создается через B, например:

    A myA = B.createA();

Можно ли создать подкласс A (назовем его SubA) и каким-то образом создать его экземпляр методом B.createA ()?

(обратите внимание, что я не могу изменить A и B ....)

Я знаю, что не все экземпляры A являются экземплярами SubA, поэтому я не могу сделать это:

    SubA mySubA = B.createA();

Точно так же я не могу разыграть это так:

    SubA mySubA = (SubA) (B.createA());

по той же причине - он получит ClassCastException.

Я плотный и забыл что-то фундаментальное, или нет никакого способа сделать это?

(Позднее добавление: мне очень жаль, я должен был упомянуть, что у A и B есть примерно по 50 методов каждый, и все, что я хочу сделать, это добавить одно свойство в SubA вместе с геттером и сеттером. I действительно лучше не реализовывать все 50 методов А для вызова соответствующего метода в объекте суперкласса.)

Ответы [ 4 ]

3 голосов
/ 30 апреля 2009

Похоже, что вам действительно нужно изменить поведение как оригинальных A, так и B. В этом случае вы можете попробовать расширить оба класса (где расширение B предназначено исключительно для указания немного другого фабричного метода для создания SubA s).

class SubA extends A {
    /** This is the one special aspect of SubA justifying a sub-class.
        Using double purely as an example. */
    private double specialProperty;

    public double getSpecialProperty() { return specialProperty; }
    public void setSpecialProperty(double newSP) { specialProperty = newSP; }

    public SubA() {
        super();
        // Important differences between SubAs and As go here....
        // If there aren't any others, you don't need this constructor.
    }

    // NOTE: you don't have to do anything else with the other methods of
    // A.  You just inherit those.
}

class SubB extends B {
    // Purely for the purposes of a slightly different factory method
    public A createA() {
        return new SubA();
    }

    // Or if you need a static method 
    // (this is usually instead of the first choice)
    public static A createA() {
        return new SubA();
    }
}

Обратите внимание, что на этом этапе вы можете создать один из ваших SubB заводских объектов и сделать его похожим на оригинальный B, например:

B myNewB = new SubB();
A myA = myNewB.createA();

Или, если вместо этого вы используете статическую фабрику, она не так близка (но близка).

A myA = SubB.createA();

Теперь, если вам действительно нужно что-то сделать с подчиненным свойством, вы получите доступ к нему через дочерний интерфейс. То есть, если вы создаете объект так:

SubA mySubA = SubB.createA();
mySubA.setSpecialProperty(3.14);
double special = mySubA.getSpecialProperty();

Изменить для обсуждения «Позднее добавление»:

На этом этапе ваш SubA-объект должен быть именно тем, что вы хотите. Он унаследует 50 методов от родителя (A), и вы можете добавить свое дополнительное свойство к потомку, а также к получателю и установщику. Я изменил код выше, чтобы проиллюстрировать, что я имею в виду.

3 голосов
/ 30 апреля 2009

Обычно это делается через прокси:

class SubA extends A {
    private A proxiedClass;

    public SubA(A a) {
        proxiedClass = a;
    }

    public int anyMethodInA() {
        return proxiedClass.anyMethodInA();
    }
}

...

SubA mySubA = new SubA(B.createA());

Выполнение этого вручную довольно многословно, поэтому большинство людей используют какую-то библиотеку AOP (например, AspectJ) для перехвата только тех вызовов методов, которые им интересны.

1 голос
/ 30 апреля 2009

Если вы просто хотите добавить поле к A без объектно-ориентированного, такого как изменение поведения, вы можете добавить его как «внешнее поле». Используйте WeakHashMap для сопоставления экземпляра A со значением поля (только если значение поля прямо или косвенно не ссылается на A или у вас возникнет проблема конфликта времени жизни объекта):

private static final Map<A,FieldType> map =
    new java.util.WeakHashMap<A,FieldType>(); // thread-safe from 1.6, IIRC

public static FieldType getField(A a) {
    return map.get(a);
}
public static void setField(A a, FieldType value) {
    map.set(a, value);
}

На самом деле мы должны использовать WeakIdentityHashMap, но его нет в библиотеке Java!

1 голос
/ 30 апреля 2009

Вы можете создать оболочку вокруг него, с SubA, имеющим конструктор, который принимает A в качестве параметра.

Как это:

SubA mySubA = new SubA(B.createA());

Поскольку все экземпляры SubA являются экземплярами A, вы можете затем присвоить его существующей переменной A и переопределить все необходимые методы.

A myA = new SubA(B.createA());

Я не могу придумать другой чистый способ сделать это.

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