overriding method with different return type

P

Paul J. Lucas

A given class Base has an nested class ParamBlock. Another class, Derived,
derived from Base also has a nested class ParamBlock. Derived's ParamBlock
is derived from Base's ParamBlock. In code:

class Base {
protected static class ParamBlock { /* ... */ }

protected Base( ParamBlock pb ) {
paramBlock_ = pb;
}

public ParamBlock param() {
return paramBlock_;
}

protected ParamBlock paramBlock_;
}

class Derived extends Base {
protected static class ParamBlock extends Base.ParamBlock {
int value;
protected ParamBlock( int v ) {
value = v;
}
}

Derived( int value ) {
super( new ParamBlock( value ) );
}

public ParamBlock param() { // illegal: wants Base.ParamBlock
return (ParamBlock)paramBlock_;
}
}

public class Test {
public static void main( String args[] ) {
Derived d = new Derived( 42 );
System.out.println( d.param().value );
}
}

I want Base's param() to return a reference to Base.ParamBlock and Derived's
param() to return a reference to Derived.ParamBlock. I am NOT looking for
polymorphism. If I call param() on an instance of a Base, I want
Base.ParamBlock; if I call param() on an instance of a Derived, I want
Derived.ParamBlock; that's it.

However, altering the return type of an inherited method in Java is illegal.
The equivalent code in C++ (below) is perfectly legal since param() isn't
virtual.

How can I get what I want in Java? I tried declaring param() final but that
didn't work.

- Paul

P.S.: Here is the equivalent (and legal) C++ code:

class Base {
protected:
class ParamBlock { /* ... */ };

Base( ParamBlock *pb ) : paramBlock_( pb ) {
}

ParamBlock *const paramBlock_;
public:
ParamBlock* param() const {
return paramBlock_;
}
};

class Derived : public Base {
protected:
class ParamBlock : public Base::paramBlock {
public:
int value;

ParamBlock( int v ) : value( v ) {
}
};
public:
Derived( int value ) : Base( new ParamBlock( value ) ) {
}

ParamBlock* param() const {
return static_cast<ParamBlock*>( paramBlock_ );
}
};

int main() {
Derived d( 42 );
int i = d.param()->value;
return 0;
}
 
F

fox_fire

The reason Java is this way is because client classes that don't know a
Base is a true Base or just a Derived in a Base reference. They might call
param thinking to get a Base.paramBlock back and get a
Derived.paramBlock.

It looks like you tried to rename the Derived's inner class to the same
name as the Base's inner class in order to get it to work. You are
avoiding the bigger problem. You can't use a method of your superclass and
throw out some garbage that client's don't expect. If you want to return
an object of Derived's inner class, give the method a different name.
 
P

Paul J. Lucas

fox_fire said:
The reason Java is this way is because client classes that don't know a
Base is a true Base or just a Derived in a Base reference. They might call
param thinking to get a Base.paramBlock back and get a
Derived.paramBlock.

It doesn't matter since a Derived.ParamBlock is-a Base.ParamBlock.
It looks like you tried to rename the Derived's inner class to the same
name as the Base's inner class in order to get it to work.

That fact is actually irrelevant. I did it for consistency.
I'm writing a compiler that emits Java code.
You are avoiding the bigger problem. You can't use a method of your
superclass and throw out some garbage that client's don't expect.

I'm not doing that. There is no garbage. There is no way for
anything "bad" to happen. Again, equivalent code in C++ is
legal (and rightly so). Java is just being overly picky.
If you want to return an object of Derived's inner class, give the method a
different name.

That's problematic for users of my compiler. I want users to
be able to say:

alpha.param().value

or:

beta.param().value

and not either of:

alpha.alphaParam().value
beta.betaParam().value

Both of those are needlessly verbose.

- Paul
 
C

Chris Uppal

Paul said:
I want Base's param() to return a reference to Base.ParamBlock and
Derived's param() to return a reference to Derived.ParamBlock.

That will be legal in 1.5 (you can try out the beta to confirm).

Be aware, however, that it is not implemented only by "relaxing" the rules of
the language (since the JVM doesn't support the change), but by making the Java
compiler automatically emit forwarding methods.

-- chris
 
P

Paul J. Lucas

Chris Uppal said:
That will be legal in 1.5 (you can try out the beta to confirm).

About time.
Be aware, however, that it is not implemented only by "relaxing" the rules of
the language (since the JVM doesn't support the change)

What the JVM does or does not support is irrelevant as far as
"relaxing" goes. As a user of Java, I couldn't care less that
Java just so happens to use a JVM on most implementations. An
implementation of Java can choose to compile directly to
machine code (e.g., gcj). From a pure Java language point of
view, the JVM is, again, irrelevant.
but by making the Java compiler automatically emit forwarding methods.

That shouldn't be necessary. In C++, when a non-virtual
function in a derived class with the same name as one in a base
class whose signature (including return-type) does not match
that of the base, it "shadows" the one in the base making it as
if it has a different name entirely.

I don't see why what would require a change in the JVM since
all the compiler has to do is internally mangle the name.

- Paul
 
C

Chris Uppal

Paul said:
As a user of Java, I couldn't care less that
Java just so happens to use a JVM on most implementations.

<shrug/> Your loss. Most of Java (the platform)'s more interesting features
are crucially dependant on, and defined by, the JVM.

-- chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top