1

假设您有一些 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 传递给 Base 的 m 方法版本,但我不明白的是......拥有像“Base b = new”这样的声明有什么意义衍生的();” ?

这似乎是解决各种类型转换问题的好方法,如果你想使用 Derived 对象,为什么不直接使用像“e”这样的声明呢?

另外,我对Java中使用的“类型”一词的含义有些困惑。今年夏天早些时候我学到的方法是,每个对象都有一个类,它对应于实例化对象时“new”后面的类的名称,但是一个对象可以有任意多的类型。例如,“e”的类型为 Base、Derived(和 Object ;)),但它的类是 Derived。这个对吗?

此外,如果 Derived 实现了一个名为 CanDoMath 的接口(同时仍在扩展 Base),那么说它具有“CanDoMath”类型以及 Base、Derived 和 Object 是否正确?

4

4 回答 4

4

我经常以以下形式编写函数:

public Collection<MyObject> foo()  {}  

public void bar(Collection<MyObject> stuff){}

在这两种情况下我都可以很容易地做到这一点ArrayList,但是如果我后来决定将表示设为 a 会发生什么Set?答案是自从我更改了方法合同以来,我有很多重构工作要做​​。但是,如果我离开它,Collection我可以随意无缝地从ArrayListHashSet。使用ArrayList它的例子有以下类型:

 Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
于 2012-08-03T00:58:11.590 回答
3

在许多情况下,不希望将自己限制在特定的(子)类中,例如您有 where 的情况e.m(8.0);。例如,假设您有一个调用的方法move在程序的坐标图中移动一个对象。但是,在您编写该方法时,您可能同时拥有由不同类处理的笛卡尔图和径向图。

如果你依赖于知道子类是什么,你就会强迫自己进入一个更高级别的代码必须知道更低级别的代码的位置,而实际上他们只是想依赖于存在具有特定签名的特定方法这一事实. 有很多很好的例子:

  • 希望将查询应用于数据库,同时不知道如何建立连接。
  • 想要对用户进行身份验证,而不必提前知道正在使用的策略。
  • 想要加密信息,而不需要在更好的加密技术出现时撕掉一堆代码。

在这些情况下,您只想确保对象具有特定类型,从而保证特定方法签名可用。这样,您的示例就是人为的;您在问为什么不只使用具有双精度作为签名参数的方法的类,而不是不可用的类。(简单地说,你不能使用没有可用方法的类。)

还有另一个原因。考虑:

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情况下这样做。这有助于确保这些事情只在正确的地方完成。

您对类型的定义很好,您对特定对象的类型的理解也很好。

于 2012-08-03T01:00:56.340 回答
2

拥有的意义Base b = new Derived();何在?

这一点是使用多态性来改变你的实现。例如,有人可能会这样做:

List<String> strings = new LinkedList<String>();

如果他们进行一些分析并发现此列表上最常见的操作对于列表类型效率低下,他们可以将其换成 ArrayList。通过这种方式,您可以获得灵活性。

如果你想使用 Derived 对象

如果您需要派生对象上的方法,那么您将使用派生对象。看看 BufferedInputStream 类——你使用它不是因为它的内部实现,而是因为它包装了一个 InputStream 并提供了方便的方法。

另外,我对Java中使用的“类型”一词的含义有些困惑。

听起来您的老师将接口和类称为“类型”。这是一个合理的抽象,作为实现接口并扩展类的类可以通过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
于 2012-08-03T01:19:40.457 回答
1

这是面向对象设计的经典问题之一。当这种情况发生时,通常意味着设计可以改进;对于这些问题,几乎总有一些优雅的解决方案......

例如,你为什么不把m需要加倍的东西拉到基类中呢?

关于你的第二个问题,一个对象可以有多个类型,因为接口也是类型,类可以实现多个接口。

于 2012-08-03T00:49:09.570 回答