扩展 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_pointer
是 Stack
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();
}
}
检查 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_pointer
是 Stack
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();
}
}