Java method implementations whose arg types are broader than declared in the interface
What is the motivation for disallowing this in Java?
interface A { void foo(C arg); } interface B { void foo(D arg); } interface C {} interface D extends C {}
public class E implements A, B { public void foo(C arg) {} }
The compiler complains that E doesn't implement the foo(D) required by B.
I realise the underlying issue is that the following doesn't compile either:
public class F implements B { public void foo(C arg) {} }
but I don't understand why Java disallows it?
Comments (8)
Henning Koch on June 4, 2005:
My wild guess: Since a class may implement an arbitrary number of interfaces, there's always a chance of namespace clashes. So Java requires you to implement the exact method signatures so you will not "accidentally" implement a method.
Maybe this could be an issue when you already have some über-generic method "foo(Object)", and later try to implement another interfaces requiring "foo(JFrame)". It is very likely that the semantics of "foo" have changed, but maybe you wouldn't even notice that your class is breaking all sorts of contracts without the Java compiler bitching.
Just a very wild guess.
Mike Pigott on June 5, 2005:
Java methods with different parameter types are different methods, even if they have the same name. For example, building a city is different from building a house, but you can build both. So:
public interface House {
void build(Wall w);
}
public interface City {
void build(Building b);
}
public class ConstructionCo implements House, City {
/* How a construction company builds a building in a city */
void build(Building b) {
}
/* How a construction company builds a wall in a house */
void build(Wall w) {
}
}
That's why you have to implement both - these two methods, while named the same, represent completely different operations.
Mike (Again) on June 5, 2005:
Sorry, I didn't realize this was rendered in pure HTML. Trying the Java example again:
<pre>
public interface House {
void build(Wall w);
}
public interface City {
void build(Building b);
}
public class ConstructionCo implements House, City {
/* How a construction company
builds a building in a city */
void build(Building b) { }
/* How a construction company
builds a wall in a house */
void build(Wall w) { }
}
</pre>
James Tauber on June 6, 2005:
Sorry, Mike. I haven't implemented formatting in comments yet.
Mike Pigott on June 6, 2005:
Don't worry about it - that's probably not a bad thing. Did I answer your question?
James Tauber on June 7, 2005:
Mike, your analogy is missing the key fact that D extends C.
Mike Pigott on June 7, 2005:
Sorry about that - I didn't see that the first time around. I think this is a case of Java not recognizing inheritance in method parameters. The following code has two print methods: one that takes an object and another that takes a string. The object print method displays "Object: " before the argument and the string version displays "String: " before the argument. The main() function creates two Strings, but assigns the first to an Object and the second to another String, then calls both print methods (more obfuscated code):
public class E {
/* Prints "Object: " + arg */
public void print(Object arg) {
System.out.print("Object: ");
System.out.println(arg);
}
/* Prints "String: " + str */
public void print(String str) {
System.out.print("String: ");
System.out.println(str);
}
/* Executes both methods */
public static void main(String[] args) {
E e = new E();
Object obj = new String("Hello, World!");
String str = new String("Hello, World!");
e.print(obj);
e.print(str);
}
}
What happens? The Object variable is treated as an Object (not a String) when determining which function to call. I think your interface inheritance problem is a symptom of that, although I didn't find anything in the Java documentation to confirm / deny that.
Add a Comment
Last Modified: June 1, 2005
Author: James Tauber
David A. Mellis on June 2, 2005:
Maybe security?
That is, maybe you've checked that you can safely pass a D to anything that implements B, but that this isn't necessarily safe on any C? So if you could implement a more lenient version of an interface, it couldn't be as strictly checked?
For example, maybe B makes the object passed to foo() available to an unauthenticated user, and it's fine to let them see D's but not C's.
Just a guess.