扩展 ArrayList - fragile-base class

Extending an ArrayList - fragile-base class

检查 Java 中的最佳实践,我们发现避免继承是一个很好的做法。以下问题可能说明了原因之一:

这里我们有一个sub-class "Stack" extends "ArrayList"

class Stack extends ArrayList
{   private int stack_pointer = 0;
    public void push( Object article )
    {   add( stack_pointer++, article );
    }
    public Object pop()
    {   return remove( --stack_pointer );
    }
    public void push_many( Object[] articles )
    {   for( int i = 0; i < articles.length; ++i )
            push( articles[i] );
    }
}

假设我们要使用前面代码中定义的 push() 添加到堆栈,然后我们要使用基数 clear() 清除堆栈 - class -即 ArrayList-

Stack a_stack = new Stack();
a_stack.push("1");
a_stack.push("2");
a_stack.clear();

The code successfully compiles, but since the base class doesn't know anything about the stack pointer, the Stack object is now in an undefined state. The next call to push() puts the new item at index 2 (the stack_pointer's current value), so the stack effectively has three elements on it—the bottom two are garbage.

所以我的问题是,为什么

base class doesn't know anything about the stack pointer

也就是说,栈指针的状态被保留在哪里?

来源:为什么扩展是邪恶的

变量 stack_pointerStack class 的成员,那么 ArrayList superclass 怎么可能知道呢?因为它不能也不会调用 clear() 不会对它做任何事情。

您需要覆盖 Stack class 中的 clear() 方法。

类似

@Override
public void clear()
{
   super.clear();
   stack_pointer = 0;
}

然后当用户在 Stack 上调用 clear() 时,它将导致指针被重置。

您还需要注意,用户可以在 Stack 上调用函数 add()insert() 等,因为它们没有被覆盖,所以会调用 ArrayList 功能。这可能不是您想要的。

更好的方法是创建一个 Stack,里面有一个 ArrayList,这样你就可以隐藏你需要隐藏的方法。

类似

public class Stack
{
    private ArrayList<Object> stack = new ArrayList<Object>();
    private int stack_pointer;

    public void push(Object obj)
    {
       stack.add(stack_pointer++, obj);
    }

    // Other methods

    public void clear()
    {
       stack.clear();
       stack_pointer = 0;
    }
}

这是其中一种方法:

import java.util.ArrayList;

class Stack extends ArrayList {

    private int stack_pointer = 0;
    private int stack_mem = 16;
    private ArrayList<Object> mem = new ArrayList<Object>();

    public void push(Object article) {
        if (stack_pointer < stack_mem) {
            mem.add(article);
            stack_pointer++;
        }
    }

    public Object pop() {
        if (stack_pointer > 0) {
            return mem.remove(stack_pointer--);
        }
        return null;
    }

    public void clear() {
        stack_pointer = 0;
        mem.clear();
    }
}