Как мне написать функцию Java, которая возвращает типизированный экземпляр 'this' и работает при расширении? - PullRequest
2 голосов
/ 23 октября 2008

Это немного ленивый вопрос, но вы получите ответ так: -)

У меня есть класс Java, который возвращает экземпляры самого себя, чтобы позволить цепочку (например, ClassObject.doStuff (). doStuff ())

Например:

public class Chainer
{
    public Chainer doStuff()
    {
       /* Do stuff ... */
       return this;
    }
}

Я бы хотел расширить этот класс. Есть ли способ, возможно, с использованием обобщений, расширить этот класс без необходимости перезаписывать сигнатуру каждого метода?

например. нет:

public class ChainerExtender extends Chainer
{
    public ChainerExtender doStuff()
    {
       super.doStuff();
       return this;
    }
}

Я пытался:

public class Chainer
{
    public <A extends Chainer> A doStuff()
    {
       /* Do stuff ... */
       return (A)this;
    }
}

public class ChainerExtender extends Chainer
{
    public <A extends Chainer> A doStuff()
    {
       /* Do stuff ... */
       return super.doStuff();
    }
}

Но это не сработало, выдав ошибку:

type parameters of <A>A cannot be determined; 
no unique maximal instance exists for type variable A with upper bounds A,Chainer

Я вынужден иметь объявления класса, такие как:

public class Chainer<T extends Chainer<T>> {}
public class ChainerExtender extends Chainer<ChainerExtender>

По этот вопрос ?

Ответы [ 7 ]

9 голосов
/ 02 ноября 2008

Вы пробовали прямо

public class Chainer
{
    public Chainer doStuff()
    {
       /* Do stuff ... */
       return this;
    }
}

public class ChainerExtender extends Chainer
{
    @Override
    public ChainerExtender doStuff()
    {
       /* Do stuff ... */
       super.doStuff();
       return this;
    }
}

В Java 5 вы можете объявить переопределяющие методы, чтобы иметь ко-вариантные возвращаемые типы, что означает только то, что это говорит: подкласс может иметь переопределенную сигнатуру метода с более конкретным типом возвращаемого значения.

6 голосов
/ 23 октября 2008

Почему бы им не вернуть интерфейс? Вам нужно будет определить все возможные цепочечные методы в интерфейсе и предоставить «нулевые» реализации в базовом классе, но вы сможете подключиться к своему сердцу.

public interface IChainer
{
  IChainer doStuff();
  IChainer doSomethingElse();
}

public class Chainer implements IChainer
{
  public IChainer doStuff()
  {
     // really do something
     return this;
  }

  public IChainer doSomethingElse()
  {
      return this; // do nothing
  }
}

public class ChainerExtender extends Chainer
{
   // simply inherits implementation for doStuff()

   public override IChainer doSomethingElse()
   {
       // really do something
       return this;
   }
}

Примечание: у меня могут быть некоторые проблемы с синтаксисом выше. Я программировал в основном на C # в течение последних нескольких лет. Надеюсь, вы поняли идею.

2 голосов
/ 23 октября 2008

Я заставил это работать таким образом. Он использует литерал класса, чтобы сигнализировать, к какому типу приводиться во время выполнения (см. Раздел 8 Общего учебника Гилада Брачи ).

public class Chainer {
  public <T extends Chainer> T doStuff (Class<T> foo) {
    /* do stuff */
    return foo.cast (this);
  }
}

public class ChainerExtender extends Chainer {
  public <T extends ChainerExtender> doOtherStuff (Class<T> foo) {
    /* do other stuff */
    return foo.cast (this);
  }
}

Итак, звонки выглядят так:

Chainer c1 = new Chainer();
c1 = c1.doStuff (c1.getClass());
// ...
ChainerExtender ce1 = new ChainerExtender();
ce1 = ce1.doStuff (ce1.getClass()).doOtherStuff (ce1.getClass());

Недостатком этого метода является то, что, если у вас есть ссылка Chainer, вы должны явно проверить, действительно ли это объект ChainerExtender, прежде чем вызывать doOtherStuff. Это, однако, имеет смысл, поскольку попытка вызова метода для объекта Chainer во время выполнения приведет к ошибке.

1 голос
/ 10 ноября 2008
1 голос
/ 23 октября 2008

Прошло некоторое время с тех пор, как я сделал какие-либо обобщения Java.

А как же:


<T>
public class  Chainer 
{
    public T doStuf()
   {
   }

}

public class ChainerDerived : extends Chainer<ChainerDerived>
{
   public ChainerDerived doStuf()
   {
   }
}

В последнее время я в основном занимался С ++, и подобные вещи - просто нормальный курс. Поддерживают ли дженерики Java этот тип структуры?

Надеюсь, я понял ваш вопрос.

1 голос
/ 23 октября 2008

Я думаю, что простой ответ "нет, ты не можешь". Я могу понять, как это может быть полезно, но я чувствую, что такое использование наследования кричит «неожиданные последствия» на меня. Это только вопрос времени, когда кто-то решит добавить немного дополнительной функциональности в подкласс, в отличие от простого выполнения super.doStuff(), и процесс управления будет чрезвычайно трудным для отслеживания.

Одно из возможных решений для ваших цепочек (если они используют шаблон builder ) для возврата новых экземпляров различных классов построителей вместо this. Посмотрите, что Стивен Колебурн сделал со сборщиками создания даты в JSR 310 , что довольно умно с точки зрения API, но неуклюже, чтобы писать.

1 голос
/ 23 октября 2008

Не могли бы вы опубликовать полный пример, приводящий к сообщению об ошибке, которое вы видите?

Я только что скомпилировал и выполнил следующее без заминок:

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ChainerExtender c = new ChainerExtender();
        c.doStuff().doStuff();
    }

    public static class Chainer
    {
        public <A extends Chainer> A doStuff()
        {
           /* Do stuff ... */
            System.out.println("Chainer");
           return (A)this;
        }
    }

    public static  class ChainerExtender extends Chainer
    {
        /** 
         * @see test.Test.Chainer#doStuff()
         */
        @Override
        public <A extends Chainer> A doStuff()
        {
            System.out.println("ChainerExtender");
           return super.doStuff();
        }
    }

}

Дает:

ChainerExtender
Chainer
ChainerExtender
Chainer

Я неправильно понял эту проблему, пожалуйста, уменьшите и прокомментируйте.

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