以下段落引自本书,#34; Thinking in Java"布鲁斯·埃克尔(Bruce Eckel)。
组合具有很大的灵活性。新类的成员对象通常是私有的,这使得使用该类的客户端程序员无法访问它们。这允许您在不干扰现有客户端代码的情况下更改这些成员。 您还可以在运行时更改成员对象,以动态更改程序的行为。接下来描述的继承没有这种灵活性,因为编译器必须对使用继承创建的类放置编译时限制。
我不明白,如何动态更改成员对象,以及它如何优于继承。 有人可以解释一下这个
答案 0 :(得分:9)
如果您的课程延长ArrayList
,则您班级的用户可以访问ArrayList
的所有公开方法。您无法更改类以扩展LinkedList,因为这可能会破坏现有代码。
public class Inheritence extends ArrayList<String>
{
// here your class depends on ArrayList implementation
}
另一方面,如果您的班级有一个类型为List
的私人会员,则您班级的用户无法直接调用该会员的方法。您可以在运行时决定是将ArrayList
实例分配给该成员,还是分配不同的List
实现(LinkedList
或其他内容)。
public class Composition
{
private List<String> member;
public Composition ()
{
// here you decide the type of your member during runtime
if (someCondition) {
member = new ArrayList<String>();
} else {
member = new LinkedList<String>();
}
}
}
答案 1 :(得分:2)
如果你使用继承,你很难看到整个画面,你必须学习很多代码才能理解发生了什么。如果你改变父类中的一个小东西,你就有麻烦了。所有的后代呢?这种变化对他们有影响吗?
还要考虑测试。父类不能分开,你必须测试一切,测试很复杂,通常根本没有,因为努力太大,无法使测试有意义。
另一方面,当你使用合成时,你可以轻松地分离功能,你可以使用依赖注入,当阅读其他人的代码时,你可以专注于较小的功能。如果使用组合的更高级别类需要通过其他实现替换一些次要功能,那么它就容易得多,因为您更好地了解代码中发生了什么。几个继承级别中的分散代码没有封装,而且通常完全不可读。
继承也很危险。例如,当您想要计算时,您向HashSet添加了多少项。您可以覆盖方法add和addAll,但是您不知道,如果addAll调用方法为每个项添加。见example
要回答原始问题 - 您可以通过构造函数或setter传递内部对象。
答案 2 :(得分:2)
查看组合和继承的更广泛方式是,组合是一种实现 has-a
关系的设计技术,而继承是一种实现 is-a
<的技术/ strong>关系。让我们进入面向对象的视角,以更好的方式理解这一点。
当我说,Dog
是 Mammal
而Snake
是 Reptile
,但两者都是{ {1}},设计可以是这样的,应该有一个名为Animals
的超类和两个名为Animal
和Mammal
的子类Reptile
和Dog
应该扩展。相应代码如下:
Snake
任何动物都可以执行这三项行动。
public abstract class Animal {
public abstract void move();
public abstract void sleep();
public abstract void reproduce();
}
所有哺乳动物/爬行动物都会以共同的方式执行此定义的功能。
public abstract class Mammal extends Animal{
@Override
public void reproduce(){
System.out.println("Gives birth!!");
}
}
public abstract class Reptile extends Animal{
@Override
public void reproduce(){
System.out.println("Lays Eggs!!");
}
}
正如您在本示例中所观察到的那样,public class Dog extends Mammal{
@Override
public void move() {
System.out.println("Uses it's limbs");
}
@Override
public void sleep() {
System.out.println("Sleeps on it's tummy");
}
}
public class Snake extends Reptile{
@Override
public void move() {
System.out.println("Crawls");
}
@Override
public void sleep() {
System.out.println("");
}
}
扩展了Dog
,Mammal
扩展了Snake
。因此,在任何给定的时间点,Reptile
始终一个Dog
而Mammal
始终一个{{1 }}。因此,是一个关系会在两个实体之间建立一个强大的债券。在执行过程中,只要Snake
延伸Reptile
,就不能是Dog
或其他任何内容。
回到 has-a 关系(撰写),我可以说Mammal
有一个 Reptile
。我没有指定此人拥有的Person
的类型。让我们回到OO的角度来更好地理解这一点。
考虑这个Car
类
Car
在这里可以看出,Person
有一个名字,地址和汽车。
汽车及相关课程的代码如下:
public class Person {
private String name;
private String address;
private Car car;
public Person(String name, String address, Car car) {
super();
this.name = name;
this.address = address;
this.car = car;
}
}
可以看出Person
和public class Car {
//Car related methods
}
public class Ferrari extends Car{
//Ferrari specific methods, attributes
}
public class Bmw extends Car{
//BMW specific attributes, methods.
}
是BMW
的类型。
现在回到声明&#34;人有一辆汽车&#34; ,汽车可以是任何汽车。它可能是法拉利或宝马或其他任何东西。这可以在创建Ferrari
对象期间传递。
Car
可以看出约翰接受了Person
Person john = new Person("John", "#81, 2nd Street,..", new Ferrari());
。在这里John可以接受在对象创建期间传递的任何类型的Ferrari
(动态性质)。因此,灵活性水平由 has-a (组合)关系提供。
我希望这可以解除你的怀疑。