方法重写必须具有与其覆盖的父方法相同的方法签名,否则它不被视为重写。
Java:
public abstract class AbstractTest {
public abstract void test() throws Exception;
}
public class ConcreteTest extends AbstractTest {
@Override
public void test() throws Exception {
}
}
ConcreteTest
(它继承了AbstractTest
)必须覆盖test()
。它们具有相同的方法名称、返回类型和没有方法参数。子类可以省略从基类抛出的异常并抛出自己的异常。子类还可以添加其他(未)检查的异常。public abstract class AbstractTest {
protected abstract void test() throws Exception;
}
public class ConcreteTest extends AbstractTest {
@Override
public void test() throws Exception {
}
}
public class B {
}
public class D extends B {
}
public abstract class Base {
public abstract B foo();
}
public class Derived extends Base {
@Override
public D foo() {
// TODO Auto-generated method stub
return new D();
}
}
Derived
返回的是D
而不是B
。为什么?因为派生类遵循父类的相同签名,并且派生类的返回类型是父类返回类型的一个子类型
。所以我可以这样写:Base pureBase = new Derived();
B b = pureBase.foo(); //which returns class D
if (b instanceof D) {
//sure, it is, do some other logic
}
在 C++ 中,你可以使用协变返回类型实现类似的效果。
C++
class AbstractTest {
public:
virtual void test() = 0;
};
class ConcreteTest : AbstractTest {
public:
void test() {
//Implementation here...
}
};
=0
结尾的纯虚函数(pure virtual function)的类被称为抽象类(Abstract class)。子类(在C++中,使用冒号:
来表示类继承关系)会覆盖(override)这个纯虚方法,但是不需要包含=0
。子类覆盖后的函数签名与父类一致。class B {
};
class D : B {
};
class Base {
public:
virtual B* foo() = 0;
}
class Derived : Base {
public:
D* foo() {
return new D();
}
}
这里使用与Java相同的推理。协变返回类型也适用于受保护和私有继承。更多关于协变返回类型的信息。
class B {} class D extends B{} class Base { public B foo() { return null;} } class Der extends Base { @Override public D foo() { return null;} }
思考一下为什么它能够工作。由于你的回答被接受,你应该修改它,以便其他人在未来得到正确的信息。 - Miserable Variable两种语言在覆盖方面的要求相似,但自然语义上存在差异。基本上,两者对调用代码(即参数)具有完全相同的约束,并提供相同或更严格的处理保证。这可能听起来有点模糊,但如果您记住这一点,它就很简单。
何时为覆盖
对于成员函数(方法)覆盖基类成员,两种语言都要求该函数是多态的(在C++中为virtual
,在Java中不是final
),具有相同的名称和相同数量和类型的参数。一些语言允许使用逆变参数类型,但Java和C++均不允许。
协变返回类型
Covariant 在这里的意思是返回类型的类型与成员函数实现的类型相同。也就是说,派生函数返回的类型必须是多态的,并且与基类中声明的类型相同或派生自该类型。Java 是一种引用语言,因此所有返回类型都可以表现出多态性,除了原始类型。C++ 是一种值语言,只有引用和指针是多态的。这意味着在 Java 中,返回类型必须完全匹配或是一个引用类型,并且派生自基类返回的类型。在 C++ 中,它必须是相同或派生类型的引用或指针。正如在介绍中所述,原因是如果通过基类调用成员函数,则会得到一个与预期相匹配的对象。
异常规范
异常说明在C++中并不常见,但在Java中很常见。 在这两种语言中,即使覆盖的方法相同:派生类中的覆盖方法必须具有更严格的限制条件,以便抛出什么。 由于Java仅验证检查后的异常,因此在派生类型中允许未在基类中抛出的未检查异常。 另一方面,衍生函数不能添加在基类中不存在的新检查异常。 协变再次发挥作用,并且派生函数可以引发协变异常。 在C ++中,异常说明具有完全不同的含义,但是以相同的方式,派生类型中的规范必须比基类中的规范更加受限制,它还允许协变异常说明。
道理是相同的,如果您在通过对基本类型的引用调用时编写了try {} catch() {}
块,并捕获在基本块中声明的所有异常,则对覆盖的调用将在相同块中捕获所有异常— —除非Java中的未检查异常。
访问修饰符
在Java中,派生方法的访问权限规定必须至少与基类相同或更加严格,也就是说,如果基类函数声明为protected,则派生函数不能为public,但可以是private。有趣的是,Java不允许你在基类中覆盖一个private函数。struct base {
virtual void f() {}
};
struct derived : private base {
void g() {
f();
}
};
struct most_derived : derived {
void f() { // overrides base::f!!!
//base::f(); // even if it does not have accesss to it
}
};
most_derived
无法访问base
子对象,从它的角度来看,它不是从base
派生的(这就是为什么most_derived::f()
内部编译失败的原因),但另一方面,通过实现一个带有签名void ()
的函数,它提供了对base::f
的覆盖。对most_derived
对象调用g()
将被分派到most_derived::f()
,而对derived
对象调用g()
将被分派到base::f()
。Java:
abstract class MyAbstract {
abstract String sayHelloTo(String name);
}
final class SayEnglish extends MyAbstract {
@Override
public String sayHelloTo(String name) {
return "Hello, " + name + "!";
}
}
final class SayLatin extends MyAbstract {
@Override
public String sayHelloTo(String name) {
return "Lorem, " + name + "!";
}
}
方法的签名(返回类型、参数类型和数量)在派生类中应该与基类完全匹配。否则,派生类也将变成抽象类。
示例:
struct foo{
virtual void foobar( int myNum) = 0;
};
struct bar: foo{
int foobar(int myNum ){}
};
int main(){
foo *obj = new bar();
return 0;
}
正如@Als所提到的,Covariant Return Type是一个例外,其中返回类型可以不同。不同意味着不同的类型应该与彼此兼容。在C++中,派生类类型的指针/引用与基类类型的指针/引用是兼容的。
来自链接的示例:
#include <iostream>
// Just create a class, and a subclass
class Foo {};
class Bar : public Foo {};
class Baz
{
public:
virtual Foo * create()
{
return new Foo();
}
};
class Quux : public Baz
{
public:
// Different return type, but it's allowed by the standard since Bar
// is derived from Foo
virtual Bar * create()
{
return new Bar();
}
};
int main()
{
Quux *tmp = new Quux();
Bar *bar = tmp->create();
return 0;
}
在Java中,你重写的方法应该与抽象方法具有相同的签名。此外,你不能限制访问权限超过父类。请参见http://download.oracle.com/javase/tutorial/java/IandI/override.html。
我假设你指的是C++。与Java一样,重写的方法签名应该与被重写的方法相匹配。请参见http://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-interface-classes/。
维基百科也有一个页面en.wikipedia.org/wiki/Method_overriding。 抽象方法可以有参数。没有任何限制。在许多情况下,传递参数可能没有意义。希望这可以帮到你 :)
在C++和Java中……
,我不认为C语言中可以有抽象方法。 - Peter Lawrey