77

I have the following class and interface:

public class BasicObject{...}
public interface CodeObject{...}

I want to create a method in which the argument needs to be of type BasicObject and implements CodeObject. I tried the following code but it doesn't guarantee clazz to be a class that implements CodeObject.

myMethod(Class<? extends BasicObject> clazz){...}

I want to do something like this but it doesn't compile:

myMethod(Class<? extends BasicObject implements CodeObject> clazz){...}
Pang
  • 9,564
  • 146
  • 81
  • 122
Gab
  • 5,604
  • 6
  • 36
  • 52
  • This seems to be answering the same question: http://stackoverflow.com/questions/745756/java-generics-wildcarding-with-multiple-classes – yiannis May 17 '12 at 19:23

4 Answers4

119

Your pattern class has to extend BasicObject and extend/implement CodeObject (which is actually an interface). You can do it with multiple classes declared in the wildcard definition of the method signature, like this:

public <T extends BasicObject & CodeObject> void myMethod(Class<T> clazz)

Note that it won't work if you do it any of these ways:

  • public <T extends BasicObject, CodeObject> void myMethod(Class<T> clazz)

    This is technically valid syntax, but CodeObject is unused; the method will accept any classes that extends BasicObject, no matter whether they extend/implement CodeObject.

  • public void myMethod(Class<? extends BasicObject & CodeObject> clazz)
    public void myMethod(Class<? extends BasicObject, CodeObject> clazz)

    These are just wrong syntax according to Java.

Matthew Read
  • 1,365
  • 1
  • 30
  • 50
bontade
  • 3,194
  • 6
  • 48
  • 75
  • 3
    about the part with the "," , what is the purpose of the "CodeObject"? the class extends from "BasicObject", but what about the "CodeObject" ? – android developer Aug 01 '13 at 07:39
  • 7
    For me, it works only if I use only T as a parameter type (without Class). Full line: `public void myMethod(T clazz)` – David Novák Dec 17 '15 at 19:40
  • then in the method, how do I call a getter belonging to `BasicObject` on the clazz object? – OrangePot Feb 28 '18 at 00:16
  • Is there a way to do this inside a layout .xml? `` ? – Cullub Mar 16 '20 at 03:36
  • Wow this even works with a constructor! – User Rebo Jan 04 '21 at 20:08
  • I was confused why `` "hides type CodeObject" and why it's "technically valid syntax" that seemingly has no purpose until I realized what it's actually doing. You're just defining two seperate types `T` and `CodeObject`, but since there already is a `CodeObject`, you're hiding the first one. It's convention to name generic types with a single capital letter, but you're not actually required to. – PianoMastR64 Nov 15 '22 at 02:50
11

Here is an approach which is a bit verbose, but avoids generics headaches. Create another class which does the extending/implementing:

public abstract class BasicCodeObject 
    extends BasicObject 
    implements CodeObject {...}

Then your method can be:

public <T extends BasicCodeObject> void myMethod(Class<T> clazz) {...}
Steve McLeod
  • 51,737
  • 47
  • 128
  • 184
9

There are two approaches to your problem depending on whether you want to pass a class type in your method argument that extends BasicObject and implements CodeObject or a class object that does so. There are solutions for both.

Solution 1:

If you want to pass the Class itself, you can do this, as explained by @bontade,

public <R extends BasicObject & CodeObject> void myMethod(Class<R> clazz)

and if you want to pass class object, you can write

public <R extends BasicObject & CodeObject> void myMethod(R clazz)

The above is the more complex way which deals with generics.

Solution 2:

The following is the simpler one. You can define an abstract class which extends the class you want to extend and implement it:

public abstract class TargetClassType extends BasicObject implements CodeObject {

}

now if you want to pass the Class itself, do

public void myMethod(Class<TargetClassType> clazz)

or if you want to pass the class object, write

public void myMethod(TargetClassType clazz)

Either of the above solutions fits your question, but the second one is simpler.

Ali Nem
  • 5,252
  • 1
  • 42
  • 41
2

If not all BasicObjects implement CodeObject, then you can use an instanceof / Class.isInstance() check in your method (see http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html):

myMethod(Class<? extends BasicObject> clazz)
{
    if (!clazz.isInstance(CodeObject))
    {
        (indicate that the call was incorrect)
    }
    ...
}
Riking
  • 2,389
  • 1
  • 24
  • 36
  • 6
    Yes but i don't want to do that. Of course any method could use myMethod(Object obj) and then use 'obj.isInstance(...)' but that's what im trying to avoid. I really want the users of my method to be forced to pass a good parameter. – Gab May 17 '12 at 19:06