3

In C++ and Java, or their respecting rules, what limits are placed on overiding abstract methods. Must you match the arguments or return type. I usually see abstract functions implemented with only a return type and no arguments, is it up to derived class to specify the rest. How does it work exactly?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
rubixibuc
  • 7,111
  • 18
  • 59
  • 98

6 Answers6

4

Method overriding must have the same method signature of the parent method it's overriding, otherwise it's not called overriding.

Java:

public abstract class AbstractTest {

    public abstract void test() throws Exception;
}

public class ConcreteTest extends AbstractTest {

    @Override
    public void test() throws Exception {

    }
}

As you can see, ConcreteTest (which extends AbstractTest) must override test(). They have the same method name, return types and no method parameters. The subclass can omit the exceptions thrown from the base class and throw its own Exception. The subclass can also add additional (un)checked exception.

As Peter Lawrey mentioned, a java interface methods are implicitly abstract method (See my SO question on Java Abstract Interface).

What is crucial here is that the method visibility cannot change in this case (as it's a hierarchical visibility, i.e. private->protected->public). This is valid though:

public abstract class AbstractTest {

    protected abstract void test() throws Exception;
}

public class ConcreteTest extends AbstractTest {

    @Override
    public void test() throws Exception {

    }
}

(The parent has a protected method and the subclass can override the same method and only has 2 choice for visibility: protected or public).

Also, Suppose you have

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();
    }

}

You will see that Derived returns a D and not a B. Why is that? That's because the derived class follows the same signature as the parent class and the return type of the derived class is a subtype of the return type of the parent class.

So, I can have this:

Base pureBase = new Derived();
B b = pureBase.foo(); //which returns class D

if (b instanceof D) {
   //sure, it is, do some other logic
}

In C++, you can get similar effect, using Covariant Return types

C++

class AbstractTest {
public:
    virtual void test() = 0;
};


class ConcreteTest : AbstractTest {
public:
    void test() {
        //Implementation here...
    }
};

In C++, a class with a pure virtual function (a virtual function that ends with a =0) is known as an Abstract class. The subclass (in C++, class extension is delimited by :) override the pure virtual method (except it doesn't contain the =0). It has the same signature has its parent class.

Going back to our Java example, suppose you have:

class B {

};

class D : B {

};

class Base {
public:
    virtual B* foo() = 0;
}

class Derived : Base {
public:
    D* foo() {
        return new D();
    }
}

The same reasoning (as explained in java) is done here. Covariant return types also works with protected and private inheritance. More on Covariant return types.

Community
  • 1
  • 1
Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • This is not correct. The standard specify's the presence of `virtual` keyword to say `Overidding`. See **C++03 Standard 10.3/2** – Alok Save Sep 15 '11 at 05:21
  • @Als, I am taking of java here. I will post the C++ version separately. – Buhake Sindi Sep 15 '11 at 05:24
  • 1
    There are at least two errors in the Java section. (i) The return type of an overriding method can be **subclass** of the return type of the superclass method. (ii) The throws clause can omit any exception thrown by the super class method or can instead declare to throw on or more subclasses of the exception throws by superclass method – Miserable Variable Sep 15 '11 at 05:56
  • @Hemal Panday, I don't quite understand what do you mean in (i). – Buhake Sindi Sep 15 '11 at 06:07
  • You should :) `class B {} class D extends B{} class Base { public B foo() { return null;} } class Der extends Base { @Override public D foo() { return null;} }` Think about why it works. Since yours is the accepted answer you should modify it so that others get the correct information in future. – Miserable Variable Sep 15 '11 at 06:17
  • You are missing **Covariant return types**, see @Mahesh's answer which explains, that. – Alok Save Sep 15 '11 at 06:18
  • @Als he is missing the similar constraint on throws clause also – Miserable Variable Sep 15 '11 at 06:21
  • @Hemal Pandya, now you made sense. I didn't understand your statement properly. – Buhake Sindi Sep 15 '11 at 06:23
  • That's fine. We are all learning :) Please do edit your answer. – Miserable Variable Sep 15 '11 at 06:27
  • My C++ is a little rusty now so I don't know if the same covariance rule applies, but I do know that the visibility can be reduced -- private function overriding pure function in base class is a widely practiced idiom. Further, the base class function need not be pure virtual. – Miserable Variable Sep 15 '11 at 06:29
  • A quibble; In Java, an overriding method can declare additional unchecked exceptions and errors. It may be worth mentioning that all methods in interfaces are implicitly `abstract` – Peter Lawrey Sep 15 '11 at 06:36
  • Thanks everyone of adding valuable inputs to this answer. I hope I've accommodated everyone nicely. :) – Buhake Sindi Sep 15 '11 at 06:47
2

I don't know about Java, but in C++ you have to specify the exact same argument types. The return type -on the other hand- is a covariant type, which means that if a pointer or reference to a type A is returned in the original function, an overriding function can return a pointer or reference to a type B as long as B is either A, or derives directly or indirectly from it.

As pointed by Als, a function must have been declared virtual in order to be overrided. Since the OP explicitly asked about abstract methods, which are defined both virtual and =0, there should be no need to point this out. However, I want to make extra clear that the overriding function does not need to be declared virtual. As the cited standard says, a member function matching the signature (with relaxed rules, as for covariant types) of a base member function declared virtual will be an override regardless of whether it is specified virtual or not. That is, overriding functions do not need to be declared virtual; abstract member functions, on the other hand, must be.

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • The standard specify's the presence of virtual keyword to say Overidding. See **C++03 Standard 10.3/2** – Alok Save Sep 15 '11 at 05:30
  • That section of the standard is actually saying that whether or not the virtual keyboard is present is irrelevant, and the function is an override nevertheless. And I'm curious how does that relate to my answer? – K-ballo Sep 15 '11 at 05:34
  • You misinterpret what the standard says perhaps. And it is relevant, because your answer does not mention `virtual`, which it should. – Alok Save Sep 15 '11 at 05:55
  • @Als: Really? 'If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and ref qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared).' is what you are quoting, and it explicitly says that its virtual 'whether or not it is so declared'. – K-ballo Sep 15 '11 at 06:00
  • Did you miss the mention of `virtual member function`? I don't see how you not see that `override` needs `virtual`? – Alok Save Sep 15 '11 at 06:02
  • 1
    Oh, I think I get you know, the member function in the **base class** has to be declared virtual. Well of course, if not there would be no overriding to do. But the OP said his method was abstract so it must have been defined with virtual ... = 0, so I still think your comment is just noise to my answer. – K-ballo Sep 15 '11 at 06:04
  • The OP asks a question and you fail to answer it conclusively and correctly, And pointing that mistake out makes you feel offended and rather than modifying your answer to be more correct, You chose to argue over and about it, so you deserve a -1. – Alok Save Sep 15 '11 at 06:07
  • I'm not offended, the OP asked about the limits imposed to overriding abstract methods which cannot be defined by a way other than virtual and = 0. The virtual keyboard in the base definition was not only implied, but out of the question. I find your comment confusing to the readed, I will modify my answer to reflect that. Thank you. – K-ballo Sep 15 '11 at 06:15
  • Please modify the answer to make it conclusive, that was the very intention of my comment, having an correct, conclusive answer. I would remove my downvote once you modify your answer. – Alok Save Sep 15 '11 at 06:17
1

Both languages are similar with respect to the requirements on overriding with the differences in semantics that are natural. Basically both require the exact same constraints on calling code (i.e. arguments) and offer the same or more strict guarantees on processing. This might sound a bit fuzzy here, but if you keep that in mind it is simple.

When is it an override

For a member function (method) to override a member of the base class both languages require the function to be polymorphic (virtual in C++, not final in Java) have the same name and the same number an type of arguments. Some languages allow for contra-variant argument types, but neither Java nor C++ do.

Covariant return type

Covariant here means that the type of the return type changes in the same way that the types on which the member function is implemented. That is, the type returned by a derived function must be polymorphic and be the same or derived from the same type declared in the base class. Java is a reference language, and thus all return types can exhibit polymorphism except primitive types. C++ is a value language, and only references and pointers are polymorphic. That means that in Java the returned type must match exactly or be a reference type and be derived from the type returned by the base. In C++, it must be a reference or pointer to the same or a derived type. As in the introduction the reason is that if you call the member function through a base you will have an object that matches what you expect.

Exception specifications

Exception specifications are not very common in C++, but they are in Java. In both languages, though the approach to overriding is the same: the overriding method in the derived class must have tighter constraints as to what can be thrown. The differences in the language surface here, as Java only verifies checked exceptions, so it will allow unchecked exceptions in the derived types that were not thrown by the base. On the other hand, the deriving function cannot add new checked exceptions not present in the base class, again, covariance comes into play, and the derived function can throw covariant exceptions. In C++ the exception specifications have a complete different meaning, but in the same way, the specification in the derived type must be more constrained than in the base and it also allows covariant exception specifications.

The rationale is the same, if you write a try {} catch() {} block around a call through a reference to the base type, and it catches all of the exceptions that are declared in the base, a call to the override will have all the exceptions caught in the same blocks --except possibly unchecked exceptions in Java.

Access modifiers

In Java the access specification to the derived method must be at least as restrictive as that of the base, that is, if the base function declaration specifies protected, then the derived function cannot be public, but can on the other hand be private, interestingly Java does not allow you to override a private function in the base class.

In C++, access specifiers don't come into play for overriding, and you can modify the access specifiers as you wish, making it more or less restrictive in the derived classes. Incidentally, you can override a private member in the base class (that is declared virtual), and that is commonly used to implement the NVI pattern (Non Virtual Interface), that has to be implemented through protected methods in Java.

Stop overriding

Java allows you to break the chain of overriding at any level, by marking the member function as final or alternatively making it private. In C++ (current standard) you cannot break the overriding chain at any point, not even in those cases where the final overrider does not have access to the member function it is overriding, which makes for a weird effect:

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
   } 
};

In that example, because inheritance is private at the derived level, most_derived does not have access to the base subobject, from its point of view, it does not derive from base (reason why base::f() will fail to compile inside most_derived::f()) but on the other hand, by implementing a function with the signature void () it is providing an override for base::f. A call to g() on a most_derived object will be dispatched to most_derived::f(), while on a derived object will be dispatched to base::f().

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
0

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 + "!";
    }
}

The same is for C++ considering syntax difference, i.e. the same signature for overridden abstract method.

Andrey Atapin
  • 7,745
  • 3
  • 28
  • 34
0

Your override methods in java should have the same signature as the abstract method you are overriding. Also you cannot restrict access more than the parent class. See http://download.oracle.com/javase/tutorial/java/IandI/override.html

I assume that you mean C++. The same as java the overriding methods signature should match the overridden. See http://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-interface-classes/

Wiki has a page too en.wikipedia.org/wiki/Method_overriding. Abstract methods can have parameters. There is not restriction on that. In many cases it might not make sense to pass parameters. Hope this helps :)

Anand
  • 75
  • 6
0

The signature of the method ( the return type, the type and number of arguments ) should exactly match in the derived class to that of the base class. Else the derived class will also become be abstract.

Example:

struct foo{
    virtual void foobar( int myNum) = 0;
};

struct bar: foo{
    int foobar(int myNum ){}    
};

int main(){

    foo *obj = new bar();
    return 0;
}

test.cc:6: error: conflicting return type specified for ‘virtual int bar::foobar(int)’
test.cc:2: error: overriding ‘virtual void foo::foobar(int)’

As @Als mentioned, Covariant Return Type is an exception where the return type can be different. By different, I mean the different types should be type compatible to each. Pointer/reference of a derived class type in C++ is type compatible with pointer/reference of the base type.

Example from the link :

#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;    
}
Mahesh
  • 34,573
  • 20
  • 89
  • 115