3

Just playing around with interfaces and I have a question about something which I can't really understand.

The following code doesn't run, which is the behaviour I expect as the interface method requires the method to work for any object and the implemented method has the signature changed to only allow string objects.

interface I {
    public void doSomething(Object x);
}

class MyType implements I {
    public void doSomething(String x) {
        System.out.println(x);
    }
}

However, using the following block of code, I was shocked to see that it did work. I thought it would not work as we are expecting to return an object and the implemented method will only return a string object. Why does this work and what is the difference between the two principles here of passed parameters and return types?

interface I {
    public Object doSomething(String x);
}

class MyType implements I {
    public String doSomething(String x) {
        System.out.println(x);
        return(x); 
    }
}
Shaded
  • 17,276
  • 8
  • 37
  • 62
mark
  • 2,841
  • 4
  • 25
  • 23

8 Answers8

3
public Object doSomething(String x);

has to return something. Anything, in fact, so long as it is some type of object. So if you implement

public String doSomething(String x) {stuff}

that's fine, because it does in fact return an Object. The fact that the object it will return will always be a String is no big deal.

The reason the first example doesn't work, is because accepting only strings is more limiting than accepting any object. But returning only strings is fine.

For an analogy, let's say you got a contract to paint a building, and you're gonna hire some employees to help you out. The contract requires that you hire any painter that applies, regardless of how tall they are, but doesn't specify what color paint to use. If you only hired painters over 6 ft tall (that's the input, accepting only String instead of all Object), you'd be violating the contract. But choosing to paint with only blue paint (returning only strings) is just fine, because the contract didn't specify color, only that you must paint the building.

DGH
  • 11,189
  • 2
  • 23
  • 24
2

It works because a String is an Object.

Colin D
  • 5,641
  • 1
  • 23
  • 35
  • 1
    Well that's not really the answer here. His first example had the same "cast". The reason it worked the second time is explained by maress – keyser May 14 '12 at 15:57
  • There is not cast in his first example. The interface declares a method that takes an object as a parameter. Having a method that takes a string is not the same as taking an Object. Could you pass an Integer to his string method, like you could with the one that takes an Object? – Colin D May 14 '12 at 16:44
  • That's more of an answer, but still not quite there :p – keyser May 14 '12 at 17:48
2

From the java language specification:

Return types may vary among methods that override each other if the return types are reference types. The notion of return-type-substitutability supports covariant returns, that is, the specialization of the return type to a subtype.

So in other words, it works as you did it, but it would not work if the return type in the interface is String, and in the implementing class is Object.

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
Christian
  • 4,543
  • 1
  • 22
  • 31
2

The principle behind this behaviour is called covariant return type. In this particular case, the overrriding type may "narrow" the originally declared parameter type.

This means that as String is subclassing Object, Object may be substituted by String.

kosa
  • 65,990
  • 13
  • 130
  • 167
kostja
  • 60,521
  • 48
  • 179
  • 224
2

This has to do with covariant return types, introduced in Java SE 5.0.
You can see more details in http://docs.oracle.com/javase/tutorial/java/javaOO/returnvalue.html

yiannis
  • 1,421
  • 12
  • 21
1

The reason why the first example doesn't work and the second example does, is because function prototypes are defined by the name and all parameters only, but not the return type. In the first example, there is a difference, so the compiler thinks they are two different functions.

In the second example, the implemented function does not broaden the type, but instead specializes the type (String is a specialization of Object), so it works.

Likewise you can limit the visibility of the implemented method, but not broaden it.

Furthermore, Java has generics, which are useful in this context.

Example:

interface I<T>
{
    public void doSomething(T x);
}

class MyType implements I<String>
{
    public void doSomething(String x)
    {
        System.out.println(x);
    }
}
Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
1

method signature does not take into account the return type. (Though is is an error to declare two methods with the same signature but different return type). So:

void doSomething(Object)
void doSomething(String)

Are simply two methods and none overrides or implements the other

maress
  • 3,533
  • 1
  • 19
  • 37
1

string class is inherited from object class, so only this code works.

Mohamed Jameel
  • 602
  • 5
  • 21