面向对象的编程练习

时间:2015-02-16 03:04:42

标签: oop object interface static

我正在检查测试并参加了此次审核练习。我有答案,但不能为我的生活包围我的头。谁能解释一下发生了什么?

interface I1 {
 public void f();
}
class A implements I1 {
 public void f() {
  //implementation
 }
 public static void g(){
  //implementation
 }
}
class B extends A {
 public void f() {
//implementation
 }
 public static void g(){
 //implementation
 }
}

假设

I1 i1 = new B();

I1 i2 = new A();

a a1 = new A();

A a2 =新B();

A b1 =(A)新B();

无需编写任何代码,用文字解释每个代码的效果 以下方法调用。只需说出"这将调用f 在A"中实现的方法等

b1.f();

b1.g();

i1.f();

a1.g();

i2.f();

a2.g();

1 个答案:

答案 0 :(得分:1)

分析g方法

请注意,f是实例方法,而g是静态方法。静态方法与class相关联。

因此,因为您将b1a1a2声明为A,请致电b1.g()a1.g()a2.g()将全部致电A.g()

这是关于Java静态方法的另一个好问题:Why doesn't the compiler complain when I try to override a static method?

分析f方法

Java实例方法是默认的virtual方法,可以被子类覆盖。那么f()实际上做什么取决于实例是什么(即取决于new关键字后面的类型)。

b1B的一个实例,因此b1.f()会调用f上定义的B方法,即B.f()

i1也是B的一个实例,因此i1.f()也会调用f上定义的B方法,即B.f() }。

i2A的一个实例,因此i2.f()会调用f上定义的A方法,即A.f()

那么a2.f()呢?如上所述,a2B的实例(您使用new关键字创建),因此a2.f()会调用B.f()

有关方法重写的更多信息

你可能想知道为什么他们这样工作?为什么a2.f()不能用于在Java中调用A.f()?这就是原因:

假设您有一个模块可以对记录列表进行复杂的计算。这些记录可以从数据库中检索,也可以从第三方rest api提供的web api中检索。如何使您的模块尽可能可扩展?

这是您的模块代码:

public class MyModule {
    public void doComplexWork() {

        // Where to get the records?
        List<Record> records = ???;

        // Do complex computation here
    }
}

可扩展的方法是将接口(或抽象类)定义为记录检索的合同

public interface IRecordRetriever {
    List<Record> retrieveRecords();
}

然后实现两个IRecordRetriever,一个用于从数据库中检索记录,另一个用于从rest api中检索记录。

public class DatabaseRecordRetriever implements IRecordRetriever {
    @Override
    public List<Record> retrieveRecords() {
        // Connect database and fetch database records here
    }
}

public class RestApiRecordRetriever implements IRecordRetriever {
    @Override
    public List<Record> retrieveRecords() {
        // Access the rest api to fetch records here
    } 
}

现在让我们更改一下模块代码:

public class MyModule {

    private IRecordRetriever _recordRetriever;

    // Inject IRecordRetriever from contructor
    public MyModule(IRecordRetriever retriever) {
        _recordRetriever = retriever;
    }

    public void doComplexWork() {

        // Where to get the records?
        // From the IRecordRetriever
        List<Record> records = _recordRetriever.retrieveRecords();

        // Do complex computation here
    }
}

现在我们的MyModule现在可以扩展了。因为无论在哪里检索记录,都不需要更改MyModule中的代码。如果现在您从数据库中检索记录,但稍后您决定更改以从rest api检索记录,则MyModule中的任何内容都不需要更改!您只需要更改传递给IRecordRetriever构造函数的运行时MyModule实例。

为什么我们可以实现这种可扩展性?这是因为方法覆盖。我们将私有字段_recordRetriever声明为IRecordRetriever,因此实际行为取决于实际的运行时实例(由new关键字创建)。因此,要更改记录检索行为,我们只需要更改传递给IRecordRetriever构造函数的运行时MyModule实例。如果方法覆盖不可用,那就是:

IRecordRetriever retrievever = new DatabaseRecordRetriever(); retriever.retrieveRecords();

如果对retriever.retrieveRecords()的调用未调用retrieveRecords()上定义的DatabaseRecordRetriever方法,但调用retrieveRecords()上定义的IRecordRetriever方法,则我们不是能够实现可扩展性。 (当然,IRecordRetriever是一个接口,因此您无法调用IRecordRetriever.retrieveRecords(),但这也适用于虚方法。)

我上面提到的其他面向对象编程语言也适用。

我通过构造函数将IRecordRetriever传递给MyModule的方式称为Constructor Dependency Injection。您可以阅读有关面向对象编程,方法覆盖和GoF设计模式的更多材料。