Расширение Java ArrayList - PullRequest
       16

Расширение Java ArrayList

15 голосов
/ 11 ноября 2011

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

Мне это кажется разумным, но я очень новичок в Java и вижу другие вопросы, которые препятствуют расширению ArrayList, например Расширение ArrayList и Создание новых методов .Я не знаю достаточно Java, чтобы понять возражения.

В своей предыдущей попытке я закончил тем, что создал в ThingContainer несколько методов, которые по сути были сквозными для ArrayList, поэтому расширение казалось более простым.

Есть ли лучший способ сделать то, что я пытаюсь сделать?Если так, как это должно быть реализовано?

import java.util.*;

class Thing {
    public String name;
    public int amt;

    public Thing(String name, int amt) {
        this.name = name;
        this.amt = amt;
    }

    public String toString() {
        return String.format("%s: %d", name, amt);
    }

    public int getAmt() {
        return amt;
    }
}

class ThingContainer extends ArrayList<Thing> {
    public void report() {
        for(int i=0; i < size(); i++) {
            System.out.println(get(i));
        }
    }

    public int total() {
        int tot = 0;
        for(int i=0; i < size(); i++) {
            tot += ((Thing)get(i)).getAmt();
        }
        return tot;
    }

}

public class Tester {
    public static void main(String[] args) {
        ThingContainer blue = new ThingContainer();

        Thing a = new Thing("A", 2);
        Thing b = new Thing("B", 4);

        blue.add(a);
        blue.add(b);

        blue.report();
        System.out.println(blue.total());

        for (Thing tc: blue) {
            System.out.println(tc);
        }
    }
}

Ответы [ 3 ]

8 голосов
/ 11 ноября 2011

Ничто в этом ответе не препятствует расширению ArrayList;была проблема с синтаксисом.Расширение класса существует, поэтому мы можем повторно использовать код.

Обычные возражения против расширения класса - это обсуждение "композиция предпочтений по наследованию" .Расширение не всегда является предпочтительным механизмом, но оно зависит от того, что вы на самом деле делаете.

Правка для примера композиции по запросу.

public class ThingContainer implements List<Thing> { // Or Collection based on your needs.
    List<Thing> things;
    public boolean add(Thing thing) { things.add(thing); }
    public void clear() { things.clear(); }
    public Iterator<Thing> iterator() { things.iterator(); }
    // Etc., and create the list in the constructor
}

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

В Groovy вы можете просто использовать аннотацию @Delegate для автоматического создания методов.Java может использовать аннотацию Project Lombok @Delegate, чтобы сделать то же самое.Я не уверен, как Lombok выставит интерфейс, или если он это сделает.

Я использую свечение, я не вижу в этом ничего принципиально неправильного с расширением - это действительно вопрос того,Решение лучше подходит для проблемы.

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

Подробнее см. Эффективную Java Блоха, пункт 16.

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

Вот конкретный пример, взятый из книги (извините, Джош!), В псевдокоде и перефразированном (все ошибки мои).

class CountingHashSet extends HashSet {
    private int count = 0;
    boolean add(Object o) {
        count++;
        return super.add(o);
    }
    boolean addAll(Collection c) {
        count += c.size();
        return super.addAll(c);
    }
    int getCount() { return count; }
}

Тогда мы используем это:

s = new CountingHashSet();
s.addAll(Arrays.asList("bar", "baz", "plugh");

И оно возвращает ... три?Нету.Шесть.Почему?

HashSet.addAll() реализовано на HashSet.add(), но это внутренняя деталь реализации.Наш подкласс addAll() добавляет три вызова super.addAll(), который вызывает add(), что также увеличивает счетчик.

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

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

0 голосов
/ 11 ноября 2011

Вот мое предложение:

interface ThingStorage extends List<Thing> {
    public int total();
}

class ThingContainer implements ThingStorage {

    private List<Thing> things = new ArrayList<Thing>();

    public boolean add(Thing e) {
        return things.add(e);
    }

    ... remove/size/... etc     

    public int total() {
        int tot = 0;
        for(int i=0; i < size(); i++) {
            tot += ((Thing)get(i)).getAmt();
        }
        return tot;
    }

}

А report() на самом деле не нужен. toString () может сделать все остальное.

0 голосов
/ 11 ноября 2011

Я не думаю, что расширение arrayList необходимо.

public class ThingContainer {

    private ArrayList<Thing> myThings;

    public ThingContainer(){
        myThings = new ArrayList<Thing>();
    }

    public void doSomething(){
         //code
    }

    public Iterator<Thing> getIter(){
        return myThings.iterator();
    }
}

Вы должны просто обернуть ArrayList в свой класс ThingContainer. Затем ThingContainer может иметь любые методы обработки, которые вам нужны. Нет необходимости расширять ArrayList; просто держи частного члена. Надеюсь, это поможет.

Вы также можете рассмотреть возможность создания интерфейса, представляющего ваш класс Thing. Это дает вам больше гибкости для расширяемости.

public Interface ThingInterface {
   public void doThing();
}

...

public OneThing implements ThingInterface {
   public void doThing(){
        //code
   }
}

public TwoThing implements ThingInterface {
   private String name;
   public void doThing(){
        //code
   }
}
...