Расширение не-универсального класса до универсального класса

#java #list #collections #queue

#java #Список #Коллекции #очередь

Вопрос:

Класс Java CircularFifoBuffer в пакете org.apache.commons.collections.buffer не является универсальным и может хранить объекты любого класса.

Я хотел бы создать обобщенную версию этого, которая может содержать только объекты класса T. Моей первой мыслью было расширить CircularFifoBuffer и просто написать новый метод ‘add’:

 public class CircularFifoQueue<T> extends CircularFifoBuffer {

    public boolean add(T data) {
        return super.add(data);
    }    

}
  

Однако при этом остается старый метод ‘add’, позволяющий добавлять объекты произвольного класса. Есть ли способ обойти это, который использует наследование, а не композицию (чтобы мне не приходилось повторно реализовывать все методы CircularFifoBuffer), но не позволяет пользователям класса добавлять объекты, отличные от T?

Ответ №1:

Одна из идей заключается в том, чтобы реализовать свой собственный буфер, который просто переносит исходный:

 public class CircularFifoQueue<T> { 
  private CircularFifoBuffer buffer = new CircularFifoBuffer();

  public boolean add(T data) {
     return buffer.add(data);
  }    

  // implement all other methods that are needed
}
  

Таким образом, внутренний буфер принимает все, но оболочка гарантирует, что могут быть добавлены только объекты T типа. Проблема: прямо сейчас буфер не реализует никакого интерфейса. Таким образом, его использование сейчас немного ограничено (вы не можете использовать его, если вам нужно отправить Buffer , например)

Комментарии:

1. Спасибо, это решение, с которым я решил пойти. Я также реализовал Queue<T> , чтобы иметь доступ к этим методам. Единственная проблема заключается в том, что я хотел бы, чтобы add(T t) метод возвращал объект, который выпадает из конца очереди, а это кажется невозможным в рамках Queue<T> интерфейса. На данный момент я только что создал новый метод addAndRetrieve(T t) , который делает то, что я хочу, хотя мне было бы интересно услышать о других решениях.

2. Отличная идея! Я бы также хотел, чтобы CircularFifoQueue реализовал Queue.

Ответ №2:

Нет, вы не можете.

Простая причина, по которой это невозможно, заключается в полиморфизме. Если бы вы могли удалить add(Object) метод, вы бы нарушили полиморфизм для CircularFifoBuffer класса.

Вот простой пример. Чтобы это работало правильно, у вашего CircularFifoQueue класса должен быть add(Object) метод.

 CircularFifoBuffer buffer = new CircularFifoQueue<String>();
buffer.add(new Object());
  

Комментарии:

1. Хорошо, спасибо. Итак, единственное решение — использовать композицию и повторно реализовать все необходимые методы?

2. Взгляните на шаблон делегирования en.wikipedia.org/wiki/Delegation_pattern Это сработало бы в вашем случае.

Ответ №3:

ответ @Vivien уже объясняет, почему на самом деле это не имеет смысла делать (для получения дополнительной информации прочитайте о принципе подстановки Лискова).

Однако вы могли бы обойти это, определив пользовательское переопределение add(Object) , которое просто генерирует исключение во время выполнения. Это не очень элегантное решение, но если вы хотите быстро исправить, то это может быть оно.

Ответ №4:

Вы можете попробовать следующий подход. Это не очень элегантно, но оно должно выполнять свою работу:

 public class CircularFifoQueue<T> extends CircularFifoBuffer {
    private Class<T> klass;
    public CircularFifoQueue(Class<T> klass) {
        this.klass = klass;           
    }

    @Override
    public boolean add(Object data) {
        T typedData = klass.cast(data);
        return super.add(typedData);
    }

    public boolean add(T data) {
        return super.add(data);
    }            
}
...
CircularFifoQueue<String> queue = new CircularFifoQueue<String>(String.class);
queue.add("hello"); // should work
queue.add(123L); // should throw ClassCastException
  

В любом случае, реализовать класс, который делегирует вызовы своих методов, не очень сложно. Любая приличная IDE автоматически сгенерирует это для вас.