假设你有一些Java代码如下:
public class Base{
public void m(int x){
// code
}
}
然后是一个子类Derived,它扩展Base如下:
public class Derived extends Base{
public void m(int x){ //this is overriding
// code
}
public void m(double x){ //this is overloading
// code
}
}
然后你有一些声明如下:
Base b = new Base();
Base d = new Derived();
Derived e = new Derived();
b.m(5); //works
d.m(6); //works
d.m(7.0); //does not compile
e.m(8.0); //works
对于那个没有编译的那个,我明白你是把一个double传递给了m方法的Base版本,但是我不明白的是......有什么意义,有一个像“Base”这样的声明b = new Derived();“ ?
这似乎是遇到各种投射问题的好方法,如果你想使用Derived对象,为什么不去寻找像“e”这样的声明呢?
另外,我对Java中使用的“type”这个词的含义感到有点困惑。今年夏天早些时候我学习它的方式是,每个对象都有一个类,它对应于实例化对象时“new”后面的类的名称,但一个对象可以有他们想要的许多类型。例如,“e”具有Base,Derived,(和Object;)类型,但其类是Derived。这是对的吗?
此外,如果Derived实现了一个名为CanDoMath的接口(同时仍在扩展Base),那么它是否具有类型“CanDoMath”以及Base,Derived和Object?是正确的吗?
答案 0 :(得分:4)
我经常以下列形式编写函数:
public Collection<MyObject> foo() {}
public void bar(Collection<MyObject> stuff){}
我可以很容易地在两个实例中都使它成为ArrayList
,但是如果我后来决定将表示设为Set
会发生什么?答案是,自从我改变方法合同后,我有很多重构要做。但是,如果我将其保留为Collection
,我可以随意从ArrayList
无缝更改为HashSet
。使用ArrayList
的示例,它具有以下类型:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
答案 1 :(得分:3)
在许多情况下,不希望将自己局限于特定(子)类,例如e.m(8.0);
所在的情况。例如,假设您有一个名为move
的方法,它可以在程序的坐标图中移动一个对象。但是,在您编写方法时,您可能同时拥有笛卡尔和径向图,由不同的类处理。
如果您依赖于知道子类是什么,那么您就强迫自己进入一个位置,其中更高级别的代码必须知道更低级别的代码,而实际上他们只是想依赖于某个特定方法的事实。存在特定的签名。有很多很好的例子:
在这些情况下,您只需要确保对象具有特定类型,这可以保证特定方法签名可用。通过这种方式你的例子是人为的;你问为什么不只是使用一个有一个方法的类,其中double是签名的参数,而不是一个不可用的类。 (简单地说,你不能使用没有可用方法的类。)
还有另一个原因。考虑:
class Base {
public void Blah() {
//code
}
}
class Extended extends Base {
private int SuperSensitiveVariable;
public setSuperSensistiveVariable(int value) {
this.SuperSensistiveVariable = value;
}
public void Blah() {
//code
}
}
//elsewhere
Base b = new Extended();
Extended e = new Extended();
请注意,在b
的情况下,我无法访问方法set()
,因此无法意外地删除超级敏感变量。我只能在e
案例中这样做。这有助于确保这些事情只在正确的地方完成。
您对类型的定义是好的,您对特定对象的类型的理解也是如此。
答案 2 :(得分:2)
拥有
Base b = new Derived();
有什么意义?
这一点是使用多态来改变你的实现。例如,有人可能会这样做:
List<String> strings = new LinkedList<String>();
如果他们进行了一些分析并发现此列表中最常见的操作对于列表类型是低效的,则可以将其交换为ArrayList。通过这种方式,您可以获得灵活性。
如果您想使用派生对象
如果您需要派生对象上的方法,那么您将使用派生对象。看看BufferedInputStream类 - 你使用它不是因为它的内部实现,而是因为它包装了一个InputStream并提供了方便的方法。
另外,我对Java中使用的“type”这个词的含义感到有点困惑。
听起来你的老师将接口和类称为“类型”。这是一个合理的抽象,因为实现接口和扩展类的类可以用3种方式引用,即
public class Foo extends AbstractFoo implements Comparable<Foo>
// Usage
Comparable<Foo> comparable = new Foo();
AbstractFoo abstractFoo = new Foo();
Foo foo = new Foo();
在不同上下文中使用的类型示例:
new ArrayList<Comparable>().Add(new Foo()); // Foo can be in a collection of Comparable
new ArrayList<AbstractFoo>().Add(new Foo()); // Also in an AbstractFoo collection
答案 3 :(得分:1)
这是面向对象设计的经典问题之一。当这样的事情发生时,通常意味着可以改进设计;对这些问题几乎总有一种优雅的解决方案....
例如,为什么不把需要加倍的m
拉入基类?
关于第二个问题,一个对象可以有多个类型,因为Interfaces也是类型,类可以实现多个接口。