Вопрос по subclass, java, superclass, interface – Java: возвращение подкласса в сигнатуре метода суперкласса

21

Я работаю над проблемой, где есть несколько реализацийFooв сопровождении несколькихFooBuilder& APOS; с. В то время какFooимеют несколько общих переменных, которые должны быть установлены, они также имеют различные переменные, которые требуют их соответствующихFooBuilder реализовать некоторые конкретные функции. Для краткости я хотел бы иметьFooBuilderсеттеры для использования цепочки методов, такие как:

public abstract class FooBuilder {
  ...

  public FooBuilder setA(int A) {
    this.A = A;
    return this;
  }

  ...
}

а также

public class FooImplBuilder extends FooBuilder{
  ...
  public FooImplBuilder setB(int B) {
    this.B = B;
    return this;
  }
  public FooImplBuilder setC(int C) {
    this.C = C;
    return this;
  }
  ...
}

И так далее, с несколькими разнымиFooBuilder Реализации. Технически это делает все, что я хочу, однако, этот подход чувствителен к порядку вызовов методов при выполнении цепочки методов. У следующего метода есть неопределенные ошибки компиляции:

someFoo.setA(a).setB(b)...

Требование, чтобы разработчик задумался о порядке вызовов методов в цепочке. Чтобы избежать этого, я бы хотел, чтобыFooBuilder каким-то образом вернуть фактический реализующий подкласс. Однако я не уверен, как это сделать. Каков наилучший подход?

Кроме того, он либо вызывает приведение повсюду, либо мешает вам изменять свойства суперклассов, когда вы этого хотите. Mark Jeronimus

Ваш Ответ

3   ответа
16

Самый простой способ справиться с этим в Java, вероятно, заключается в использовании обобщений, как упоминалось в ответе Йохена.

В этой записи блога есть хорошее обсуждение проблемы и разумное решение.Использование наследования с плавными интерфейсами, который объединяет дженерики с определениемgetThis() метод, переопределенный в подклассах компоновщика, чтобы решить проблему всегда возврата компоновщика правильного класса.

Просто суммируем связанный блог и удалим отдельных сборщиков:public abstract class X<B extends X<?>>{public int a;public B setA(int foo){this.a=foo;return getThis();}public abstract B getThis();}public class Y extends X<Y>{public int b;public Y setB(int bar){this.b=bar;return getThis();}public Y getThis(){return this;}}
Мне нравится это решение. Гораздо более сложный, чем я предлагал, но также более гибкий и в конечном итоге элегантный.
4

Если вы объявите setA () что-то вроде этого (псевдокод)

<T> T setA(int a)

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

obj.<RealFoo>setA(42)
@DonRoby: Почему вы не публикуете это как ответ? Это лучшее решение, чем у Йохена, и оно, безусловно, заслуживает одобрения ;-)
+1: хорошее описание этой тактики наegalluzzo.blogspot.com/2010/06/…
@meriton: Готово!
Компиляторnot в состоянии выяснить тип в вызове метода цепочки, и повторение типа для каждого вызова метода вряд ли вариант.
2

этот отличный ответ Я сейчас делюсь этим.

public class SuperClass<I extends SuperClass>
{
    @SuppressWarnings( "unchecked" ) // If you're annoyed by Lint.
    public I doStuff( Object withThings )
    {
        // Do stuff with things.
        return (I)this ; // Will always cast to the subclass. Causes the Lint warning.
    }
}

public class ImplementationOne
extends SuperClass<ImplementationOne>
{} // doStuff() will return an instance of ImplementationOne

public class ImplementationTwo
extends SuperClass<ImplementationTwo>
{} // doStuff() will return an instance of ImplementationTwo

Похожие вопросы