面向对象编程练习
Object Oriented Programming exercise
我正在复习考试并参加了这次复习练习。我有答案,但终生无法解决这个问题。谁能解释一下这是怎么回事?
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=新 B();
I1 i2=新 A();
A a1=新A();
A a2=新 B();
A b1= (A) new B();
不写任何代码,用文字说明每一个的作用
下面的方法调用。只需说“这将调用 f
在 A" 等中实现的方法
b1.f();
b1.g();
i1.f();
a1.g();
i2.f();
a2.g();
g
方法解析
注意f
是实例方法,而g
是静态方法。静态方法与 class
.
关联
因此,因为您将 b1
、a1
和 a2
声明为 A
,调用 b1.g()
、a1.g()
和 a2.g()
将全部调用 A.g()
.
这是关于 Java 静态方法的另一个很好的 SO 问题:Why doesn't the compiler complain when I try to override a static method?
f
方法解析
Java实例方法默认是virtual
方法,可以被子classes覆盖。所以 f()
实际上做什么取决于实例是什么(即取决于 new
关键字后面的类型)。
b1
是B
的一个实例,所以b1.f()
会调用B
上定义的f
方法,即B.f()
.
i1
也是B
的一个实例,所以i1.f()
也会调用B
上定义的f
方法,即B.f()
.
i2
是A
的一个实例,所以i2.f()
会调用A
上定义的f
方法,即A.f()
.
那么a2.f()
呢?如上所述,a2
是 B
的一个实例(您使用 new
关键字创建它),因此 a2.f()
将调用 B.f()
.
关于方法覆盖的更多信息
您可能想知道为什么它们会这样工作?为什么 a2.f()
没有设计成在 Java 中调用 A.f()
?原因如下:
假设您有一个模块可以对记录列表进行复杂的计算。这些记录可以从数据库中检索,也可以从第三方 rest api 提供的网络 api 中检索。如何让你的模块尽可能的可扩展?
这是您的模块代码:
public class MyModule {
public void doComplexWork() {
// Where to get the records?
List<Record> records = ???;
// Do complex computation here
}
}
一种可扩展的方法是将接口(或抽象 class)定义为 contract of 'record retrieving':
public interface IRecordRetriever {
List<Record> retrieveRecords();
}
然后你实现两个 IRecordRetriever
s,一个从数据库中检索记录,而另一个从 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
中仍然没有任何内容需要更改!您只需要更改传递给 MyModule
构造函数的运行时 IRecordRetriever
实例。
为什么我们可以实现这种扩展性?这是因为方法覆盖。我们将私有字段 _recordRetriever
声明为 IRecordRetriever
,因此实际行为取决于实际的运行时实例(由 new
关键字创建)。因此,要更改记录检索行为,我们只需要更改传递给 MyModule
构造函数的运行时 IRecordRetriever
实例。如果方法覆盖不可用,即:
IRecordRetriever 检索器 = new DatabaseRecordRetriever();
retriever.retrieveRecords();
如果对retriever.retrieveRecords()
的调用不是调用DatabaseRecordRetriever
上定义的retrieveRecords()
方法,而是调用IRecordRetriever
上定义的retrieveRecords()
方法,我们无法实现可扩展性。 (当然,IRecordRetriever
是一个接口,所以你不能调用IRecordRetriever.retrieveRecords()
,但这也适用于虚拟方法)。
我上面提到的也适用于其他面向对象的编程语言。
我通过构造函数将 IRecordRetriever
传递给 MyModule
的方式称为 Constructor Dependency Injection
。您可以阅读更多有关面向对象编程、方法覆盖和 GoF 设计模式的资料。
我正在复习考试并参加了这次复习练习。我有答案,但终生无法解决这个问题。谁能解释一下这是怎么回事?
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=新 B();
I1 i2=新 A();
A a1=新A();
A a2=新 B();
A b1= (A) new B();
不写任何代码,用文字说明每一个的作用 下面的方法调用。只需说“这将调用 f 在 A" 等中实现的方法
b1.f();
b1.g();
i1.f();
a1.g();
i2.f();
a2.g();
g
方法解析
注意f
是实例方法,而g
是静态方法。静态方法与 class
.
因此,因为您将 b1
、a1
和 a2
声明为 A
,调用 b1.g()
、a1.g()
和 a2.g()
将全部调用 A.g()
.
这是关于 Java 静态方法的另一个很好的 SO 问题:Why doesn't the compiler complain when I try to override a static method?
f
方法解析
Java实例方法默认是virtual
方法,可以被子classes覆盖。所以 f()
实际上做什么取决于实例是什么(即取决于 new
关键字后面的类型)。
b1
是B
的一个实例,所以b1.f()
会调用B
上定义的f
方法,即B.f()
.
i1
也是B
的一个实例,所以i1.f()
也会调用B
上定义的f
方法,即B.f()
.
i2
是A
的一个实例,所以i2.f()
会调用A
上定义的f
方法,即A.f()
.
那么a2.f()
呢?如上所述,a2
是 B
的一个实例(您使用 new
关键字创建它),因此 a2.f()
将调用 B.f()
.
关于方法覆盖的更多信息
您可能想知道为什么它们会这样工作?为什么 a2.f()
没有设计成在 Java 中调用 A.f()
?原因如下:
假设您有一个模块可以对记录列表进行复杂的计算。这些记录可以从数据库中检索,也可以从第三方 rest api 提供的网络 api 中检索。如何让你的模块尽可能的可扩展?
这是您的模块代码:
public class MyModule {
public void doComplexWork() {
// Where to get the records?
List<Record> records = ???;
// Do complex computation here
}
}
一种可扩展的方法是将接口(或抽象 class)定义为 contract of 'record retrieving':
public interface IRecordRetriever {
List<Record> retrieveRecords();
}
然后你实现两个 IRecordRetriever
s,一个从数据库中检索记录,而另一个从 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
中仍然没有任何内容需要更改!您只需要更改传递给 MyModule
构造函数的运行时 IRecordRetriever
实例。
为什么我们可以实现这种扩展性?这是因为方法覆盖。我们将私有字段 _recordRetriever
声明为 IRecordRetriever
,因此实际行为取决于实际的运行时实例(由 new
关键字创建)。因此,要更改记录检索行为,我们只需要更改传递给 MyModule
构造函数的运行时 IRecordRetriever
实例。如果方法覆盖不可用,即:
IRecordRetriever 检索器 = new DatabaseRecordRetriever(); retriever.retrieveRecords();
如果对retriever.retrieveRecords()
的调用不是调用DatabaseRecordRetriever
上定义的retrieveRecords()
方法,而是调用IRecordRetriever
上定义的retrieveRecords()
方法,我们无法实现可扩展性。 (当然,IRecordRetriever
是一个接口,所以你不能调用IRecordRetriever.retrieveRecords()
,但这也适用于虚拟方法)。
我上面提到的也适用于其他面向对象的编程语言。
我通过构造函数将 IRecordRetriever
传递给 MyModule
的方式称为 Constructor Dependency Injection
。您可以阅读更多有关面向对象编程、方法覆盖和 GoF 设计模式的资料。