Interface implementation with method argument superclasses - java

As a practical example of the general question in the subject, I'd like to implement the containsAll method in the Set interface with
public boolean containsAll(Iterable<?> c) { /* ... */ }
I figure this should be allowed, since Collection is Iterable meaning such a containsAll would cover the interface requirement. Likewise, more generally being able to implement interfaces with argument superclasses seems like it should work.
However, Eclipse says no way (haven't tried just javac straight-up) - can someone explain the reason for that? I'm sure there's something in the spec which makes it the way it is, but I'd like to understand the motivation for requirement as well. Or am I missing something like Iterable<?> not being a superclass of Collection<?>?
As a side question - given I'm declaring two methods would the method with the Iterable signature always be preferred on calls with a Collection argument?
Eclipse Error:
If I remove the method with the Collection signature, just leaving the Iterable one (see after error), I get the following:
The type BitPowerSet must implement the inherited abstract method Set<Long>.containsAll(Collection<?>)
The exact implementation being:
#Override public boolean containsAll(Collection<?> c) {
for (Object o : c) if (!contains(o)) return false;
return true;
}
public boolean containsAll(Iterable<?> c) {
for (Object o : c) if (!contains(o)) return false;
return true;
}

Since the interface you are implementing declares the (abstract) method containsAll(Collection<?>), you must implement it with this exact signature. Java does not allow you to implement/override a method with a wider parameter type than the original. This is why you get the error you show when you comment out your method with the Collection signature.
You don't show the other error you claim to get when the method is not commented out, but I guess it might have to do something with ambiguous method overloading.

My guess as to why java has this restriction is, say you have:
class A {
void foo(String s) { ... }
}
class B extends A {
// Note generalized type
#Override void foo(Object s) { ... }
}
Now if you have class C extends B and it wants to override foo, it's not clear what argument it should take.
Say for example C extended A directly at first, overriding void foo(String s), and then it was changed to extend B. In this case C's existing override of foo would become invalid because B's foo should be able to handle all Objects, not just Strings.

The argument types are part of the method signature so the jvm needs a method with exact the same signature to find overrides. A containsAll( Iterable) will have a different signature than containsAll(Collection).
If I remember right the compiler has to use some workarounds to make generics work in spite of this limitation.
To your second question, the compiler would prefer the Collection argument since it is a subtype of Iterable, this makes the Collection method more specific than the Iterable one.

Related

Java original class method call from polymorph list

I have an abstract superclass
public abstact class SuperClass
2 classes extend this superclass
public class ChildClass1 extends SuperClass
public class ChildClass2 extends SuperClass
I have an arraylist
List<SuperClass> objects = new ArrayList<SuperClass>();
I have a method that has 2 different parameter list
public void get(ChildClass1 o)
public void get(ChildClass2 o)
When trying something like this:
for (SuperClass o : objects)
get(o);
I got an error: "The method get() is not applicable for the arguments SuperClass"
How should I implement this in a way that every element in the list could call the right method? Is the only way I can implement this is with instanceof and casting it or is there a nicer way?
This is not possible as Java decides at compile time which one of these methods to use. You could have a get method defined in SuperClass, then the code would look like this:
for (SuperClass o : objects)
o.get();
A quick Stackoverflow search lead me to this thread there is more information for you: overloading and compile time binding. Someone proposed to use the visitor pattern there. That might be an alternative, too. As every Java developer encounters the issue you have asked sooner or later I am sure there are plenty of interesting threads on this topic to be found here on Stackoverflow.
Java binds (most) method invocations dynamically with respect to the object on which they are invoked, but it determines type signatures statically. Thus, when your code ...
for (SuperClass o : objects)
get(o);
... is analyzed at compile time, Java must select a get() method signature based only on the information it has at that time, which is that o is a SuperClass. Moreover, it will choose exactly one method signature, for an existing method on the relevant class. At run time, it will resolve that method signature against the class of the object on which it is invoked, considering other signatures only to the extent needed for supporting covariant return types.
That you have a requirement such as you do creates a distinct code smell. It makes sense to collect objects in a List<SuperClass> only for purposes that are served equally well (and perhaps polymorphically) by any SuperClass, which is not the case for you. If you cannot change that aspect of the design, then there are multiple ways you could proceed, including ...
An ugly hack way
Use instanceof and casting:
for (SuperClass o : objects) {
if (o instanceof ChildClass1) {
get((ChildClass1) o);
} else if (o instanceof ChildClass2) {
get((ChildClass2) o);
} else if (o == null) {
// do something appropriate
} else {
throw new SomeKindOfException(
"Don't know what to do with a " + o.getClass());
}
}
Double dispactch / inversion of control
Give the superclass a method ...
abstract void acceptGetter(ClassThatGets objectThatGets);
... and have each subclass implement it the same way:
void acceptGetter(ClassThatGets objectThatGets) {
objectThatGets.get(this);
}
Then replace your problematic loop with ...
for (SuperClass o : objects)
o.acceptGetter(this);
... where it is assumed that that code appears in class ClassThatGets, which declares the get() methods you described, or in a subclass of that class.
There are surely other alternatives as well, some of which may fit more cleanly into your overall design.

Can I work with generic types from a calling class?

I'm currently brushing up my Java and reading up on Generics. Since they were not treated extensively in my Java class, I'm still having some trouble wrapping my mind about it, so please keep that in mind when answering.
First of all, I'm pretty sure that what I'm trying to is not possible. However, I'd like to find out where my thinking is wrong and how I should go about achieving what I want.
What I'm trying to do is manipulating an object that implements a generic interface from another class that has no knowledge about the instantiated type. Thus, I have something like the following classes:
public interface CalledInterface<E> {
public E get() { ... }
public set(E e) { ... }
}
public class Called implements CalledInterface<String> {
...
}
Now what I want to do is:
public class Caller {
protected CalledInterface<?> c;
public Caller (CalledInterface<?> arg) {
c = arg;
}
public void run(){
// I can do this:
c.set(c.get());
// But I'd want to be able to do something like:
<?> element = c.get();
c.set(element);
}
}
What is the fundamental flaw in my thinking, if there is one? And what approach should I rather be taking?
First of all, keep in mind that generics is a compile time thing not a runtime.
Now in your Caller you defined Called c. Called is defined to implement CalledInterface<String>, so automatically, Called has the following methods generated at compile time:
String get();
void set(String e); //i assume you wanted to return void
So essentially this doesn't really make sense:
<?> element = c.get();
The Caller class isn't even aware Called is using generics internally, for it, Called just deals with strings.
UPDATE
Based on your comment, since you don't want Caller to use Called directly but use CalledInterface first thing you have to do is change the type of c to that. In this case you should not use generics, because the whole point of generics is that the same class is used in different scenarios with different types (again determined at compile time), enforcing types without having repeated code.
If I understand correctly you don't want to restrict Caller to use String, so what you have to do is change CalledInterface to not use generics, and change the methods to:
Object get();
void set(Object o);
This is how we used to do things before Generics in Java 1.4. You obviously run the risk of not having type safety, so think through whether what you want really makes design sense, because it probably does not because you have to do instanceof anyway to check the type to use the Object in a useful way (i.e. to access its methods).
If on the other hand you just change the c member (and the constructor argument of Caller) to:
CalledInterface<String> c;
Your Caller will be interacting with the CalledInterface rather than the implementation and at the same time still be type safe. So you can still pass an instance of Called and set it to c.
After your edit:
// I can do this:
c.set(c.get());
No you can't. It won't compile with c being CalledInterface<?>. (Have you even tried it?)
To do this, you can use a "capture helper":
private static <T> void helper(CalledInterface<T> c) {
c.set(c.get());
}
public void run(){
helper(c);
}
Which also solves your second problem:
private static <T> void helper(CalledInterface<T> c) {
T element = c.get();
c.set(element);
}
public void run(){
helper(c);
}
There are a few minor mistakes in your code:
protected Called c;
public Caller (CalledInterface arg) {
c = arg;
}
You are not allowed to assign arg here, because the type CalledInterface is not a subtype of Called (it is the other way around)
Also you should give type information when using CalledInterface (it is allowed to leave it out, but only for legacy purposes).
Now to the part you are wondering about. For the type Called, the compiler knows get() returns a String, if you are not interested in that, you can of course always use Object as the type of element. The compiler also knows that set() takes a String as argument, so it requires you to give one. In generics is essentially the same as using Object in a case without generics (even though it isn't allowed on the location you use it, because it doesn't make sense). This means that you would be telling the compiler to forget the type on the first line (calling get()) and to unforget it on the line below.

Java - Interface Methods

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);
}
}
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.
It works because a String is an Object.
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
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.
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.
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);
}
}
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
string class is inherited from object class, so only this code works.

Why can't parameter types be loosened in overridden methods?

This code is invalid:
interface Foo
{
public void foo(final String string);
}
public class Bar implements Foo
{
// Error: does not override.
#Override public void foo(final Object object)
{
}
}
Because every String is obviously an Object, I would expect this code to be perfectly fine: any code that depends on giving foo() a String would stil function when foo() actually takes an Object.
It appears, though, that method signatures have to be identical to those of the methods they're overriding. Why?
What if
interface Foo
{
void foo(String string);
void foo(Object string);
}
Then which method is overridden by Bar?
'loosening' as you put it should not impact on someone expecting to use your interface defined method in a particular way, as any methods you call on that object should in theory be callable on the specified object, but Eugene's point stands, becaause there would probably be a little compiler headache to deal with in determining what method you actually intended to override if you just vaguely want to stick to the interfaces specification. Also, why whould this be desireable if you are going to stick to moving up the inheritance heirarchy, because surelely you will be able to do all you want to the thing further down the hierarchy as to 'Object' for example? Possibly casting inside your method would solve your problem? If it is possible to do what you want to do, you will also probably start treading on the polymorphism paradigm.
I think this is a classical contravariance issue. Your interface requires a string to be passed as parameter, you want an implementation that accepts an object (because, after all, strings are also objects).
The problem is that if you allowed that, then you could no longer guarantee that the parameter being required by the interface is a string or any one of its ancestors. You might just as well pass any object to your implementation and you would be breaking the contract of the interface, putting in danger the type safety and type coherence of your design.
You do have options, though:
public class Bar implements Foo
{
#Override public void foo(final String object)
{
}
public void foo(final Object object)
{
foo((String) object);
}
}
By this, you would be ensuring that object is actually a string, making possible to the type system to check that you are actually complying with the interface contract established in the method signature.
Is there a particular scenario in which you would consider your contravariance example to be requirement?
It's just the constructs of the Java programming language. The structure of Java programs will grow on you. So for now just try and adjust.

Checking generic type

The Java Collections interfaces (for example, List or Set) define the contains method to accept any Object.
public boolean contains(Object o)
However, when it comes to implementing this method, the particular collection I'm working on requires that I have a type which is compatible with the generic type E of the class (i.e. either be class E, or a subclass of E, or a class which implements E if E is an interface). In other words, if o is castable to type E, then it is compatible. This presents an issue because Java erases the Generic type information, so something like this isn't possible:
public boolean contains(Object o)
{
if(o instanceof E) // compile error due to type erasures
{
// ... check if this collection contains o
}
return false;
}
My question is what would be the best way to accomplish something like this? The Oracle Article on Type Erasures mentions that this forbidden, but does not offer any solutions to this problem.
I can only think of one semi-elegant way to get around this:
Make a cast to type E. If the cast fails, o cannot be type E or a subclass of type E.
public boolean contains(Object o)
{
try
{
E key = (E) o; // I know it's unsafe, but if o is castable to E then the method should work
// check if this collection contains key
}
catch(ClassCastException e)
{
// invalid type, cannot contain o
}
return false;
}
While this would work, it looks messy (I'm not a big fan of using Exceptions in this manner).
Is there a better way to accomplish this same goal (changing the method signature is not allowed)?
edit: yeah, this doesn't work because E gets erased to Object :(
This is only possible by (1) passing in the expected Class, or (2) examining the generic type parameters of some reflective element that defines <E>. Let me elaborate.
The most common case is to simply require the caller to pass in the runtime class of E.
public class MyClass<E> {
private final Class<E> realType;
public MyClass(Class<E> realType) {
this.realType = realType;
}
public boolean Contains(Object o) {
E e = realType.cast(o); // runtime cast - will throw ClassCastException.
// Could also use realType.isInstance(o)
// or realType.isAssignableFrom(o.getClass())
...
}
}
Caller:
new MyClass<MyObject>(MyObject.class)
This is generally type safe since the compiler will verify that the <E>'s match. Of course the caller can bypass the compiler's checks...nothing you can do about that!
For (2), what I mean is that you can use reflection to examine static generic type parameters. This probably isn't a good option in your case because you must have access to some field, method, or superclass declaration that statically defines <E>. The most common way is to make your class abstract and have callers extend it. This approach is used by Hamcrest's TypeSafeMatcher (see ReflectiveTypeFinder) to great effect. (Note that TypeSafeMatcher is basically just making option (1) easier for the programmer; it still provides a constructor that takes the Class for cases when reflection doesn't work!) If you want to get really fancy, you can inspect getClass().getGenericSuperclass().getActualTypeArguments(). This isn't as easy as it sounds -- see this good article. I'm not even sure that article covers all the edge cases -- you're basically reimplementing the compiler! So just go with option (1) and be happy you're not using generics in C# :-)
I don't get why you are messing with .contains() in this way. The specification of contains() in Collection says that it returns true if the given object equals (in the sense of .equals()) some element of the container. So are you changing the meaning of .contains()? How is your equality defined?

Categories

Resources