How can I determine if a Java generic implements a particular interface?
I have tried a few different approaches, but they all results in a "cannot find symbol ... variable T" error.
First Attempt
public abstract class AbstractClass<T> {
public void doFoo() {
if (T instanceof SomeInterface){
// do stuff
}
}
}
Second Attempt
public abstract class AbstractClass<T> {
public void doFoo() {
if (SomeInterface.class.isAssignableFrom(T)) {
// do stuff
}
}
}
You can't.
Workaround 1
Add a constructor taking the class of the object.
public abstract class AbstractClass<T> {
private Class<T> clazz;
public AbstractClass(Class<T> clazz) {
this.clazz = clazz;
}
public void doFoo() {
if (clazz instanceof SomeInterface){
// do stuff
}
}
}
Workaround 2
Add a constraint to the type T with T extends SomeInterface, thereby restricting the type T to be a subtype of SomeInterface.
public abstract class AbstractClass<T extends SomeInterface> {
public void doFoo() {
// do stuff
}
}
Put a bound on it.
public abstract class <T extends SomeInterface> { }
Then you're guaranteed that it will be at least SomeInterface when passed through as an argument to a method.
From your approach, there's no way to do it, since you don't reference an instance of T anywhere in your code. If you added an argument to it, you'd be able to at least get further.
public void doFoo(T obj) { }
Related
Here's a simplified version of the object model that I am working on.
public class GenericsTest {
public interface FooInterface {
public void foo();
}
public class Bar implements FooInterface {
public void foo() {}
}
public interface GenericInterface <T> {
public T func1();
}
public class Service implements GenericInterface<Bar> {
#Override
public Bar func1() {
return null;
}
}
public class GenericBar <S extends GenericInterface<FooInterface>> {
public S s;
public GenericBar() {}
}
public static void main(String[] args) {
GenericBar<Service> serviceGenericBar; // <-- compilation error at this line
<... more code ...>
}
}
Compiler Error: type argument GenericsTest.Service is not within bounds of type-variable S
IDE (intellij) shows some more details on the error: Type parameter 'GenericsTest.Service' is not within its bound; should implement GenericsTest.GenericInterface<GenericTests.FooInterface>
Service class is implementing the GenericInterface. I have looked at few other SO questions with the same error but they don't offer clues for this particular scenario. Any ideas on how to fix this?
The problem is exactly what two compilers have told you: type Service is not within the bounds that type GenericBar requires of its type parameter, S. Specifically, GenericBar requires the S parameter of its realizations to be bound to a type that extends GenericInterface<FooInterface>. Service does not satisfy that requirement.
Service implements GenericInterface<Bar>, which is neither GenericInterface<FooInterface> nor an extension of that type, the fact that Bar implements FooInterface notwithstanding. You cannot assign a List<String> to a variable of type List<Object>, either, for basically the same reason.
You can resolve the compilation error by modifying the definition of class GenericBar like so:
public class GenericBar <S extends GenericInterface<? extends FooInterface>> {
public S s;
public GenericBar() {}
}
Whether this is what you actually want to use is an entirely different question, which only you can answer.
When you change Service to implement GenericInterface the code would compile.
public class Service implements GenericInterface<FooInterface> {
#Override
public Bar func1() {
return null;
}
}
Or if you prefer to limit Service to base only on Bar you could change GenericBar so it will become more generic:
public class Service implements GenericInterface<Bar> {
#Override
public Bar func1() {
return null;
}
}
public class GenericBar<S extends GenericInterface<? extends FooInterface>> {
public S s;
public GenericBar() {
}
}
Consider the following abstract class
public abstract class AbstractAssembler {
public abstract <T extends AbstractValue> void transform(T value);
}
and the following extension:
public class MyAssembler extends AbstractAssembler {
#Override
public void transform(MyValue value) {
// ...
}
}
With MyValue
public class MyValue extends AbstractValue {
// ...
}
Eclipse tells me:
The method transform(MyValue) of type MyAssembler must override or implement a supertype method
Why does this not work?
Your transform() method must override the generic abstract transform() method in the super class, i.e. you should have something like:
public class MyAssembler extends AbstractAssembler {
#Override
public <T extends AbstractValue> void transform(T value) {
}
}
If you want to invoke it with an actual type (e.g. MyValue) you should do:
MyValue value = new MyValue();
new MyAssembler().transfer(value);
where explicitly specifying the type-parameter (new MyAssembler().<MyValue>transfer(value);) is optional, as it will be inferred by the compiler.
If you however wish MyAssember's transform method to work only with MyValue, then you should do:
public class MyAssembler extends AbstractAssembler<MyValue> {
#Override
public void transform(MyValue value) {
}
}
Ok. Let's say I have following small Interface:
public interface MyInterface {
void interfaceMethod();
}
and a class which implements this interface:
public class GenericsTest implements MyInterface {
#Override
public void interfaceMethod() {
// do something
}
}
That's simple!
Now I have also another class which use a generic type <T extends MyInterface>:
public class AnotherClass<T extends MyInterface> {
public void doSomethingWith(T obj) {
System.out.println(obj.toString());
}
}
And now the point which I don't understand. If I want call the AnotherClass.doSomethingWith(T) method like in the following code snippet (this class is wrong; please see my edit below):
public class ClassWithError {
public ClassWithError(AnotherClass<? extends MyInterface> another) {
another.doSomethingWith(another);
}
}
I get following error:
The method doSomethingWith(capture#1-of ? extends MyInterface) in the type
AnotherClass<capture#1-of ? extends MyInterface> is not applicable for the
arguments (AnotherClass<capture#2-of ? extends MyInterface>)
Why That?
EDIT
Ohhhh nooo! My sample is wrong! ... grrrrrr ... SORRY!!
The ClassWithError must be right:
public class ClassWithError {
public ClassWithError(AnotherClass<? extends MyInterface> another, GenericsTest test) {
another.doSomethingWith(test);
}
}
And then the error is:
The method doSomethingWith(capture#1-of ? extends MyInterface) in the type
AnotherClass<capture#1-of ? extends MyInterface> is not applicable for the
arguments (GenericsTest)
AnotherClass#doSomethingWith is waiting for a parameter of type T, i.e. a subtype of MyInterface. In ClassWithError, you're passing an instance of AnotherClass, which does not fulfil this contract.
Either change doSomethingWith signature to (example):
public void doSomethingWith(AnotherClass<?> obj)
Or change body of ClassWithError to (example):
public ClassWithError(AnotherClass<GenericsTest> another) {
GenericsTest instance = /* ... */;
another.doSomethingWith(instance);
}
EDIT
With your new snippet, parameterizing your constructor could be a generic solution:
public class ClassWithError {
public <T extends MyInterface> ClassWithError(AnotherClass<T> another, T test) {
another.doSomethingWith(test);
}
}
If you need to be sure T is a GenericsTest, then use:
public class ClassWithError {
public <T extends GenericsTest> ClassWithError(AnotherClass<T> another, T test) {
another.doSomethingWith(test);
}
}
Or even simply:
public class ClassWithError {
public ClassWithError(AnotherClass<GenericsTest> another, GenericsTest test) {
another.doSomethingWith(test);
}
}
You attempted to use an upper bound generic type outside the scope of a generic type declaration. It's a little difficult to explain without seeing it and it's possible my terminology may be a bit off.
How would the compiler know what generic type AnotherClass<? extends MyInterface> actually is?
public class ClassWithError {
public ClassWithError(AnotherClass<? extends MyInterface> another, GenericsTest test) {
another.doSomethingWith(test);
}
}
You have to capture that generic type somewhere, or specify it explicitly.
Either:
public class ClassWithError<T extends MyInterface> {
public ClassWithError(AnotherClass<T> another, GenericsTest test) {
another.doSomethingWith(test);
}
}
or
public class ClassWithError {
public ClassWithError(AnotherClass<MyInterface> another, GenericsTest test) {
another.doSomethingWith(test);
}
}
please change your AnotherClass like below code.
public class AnotherClass<T extends MyInterfacee> {
public void doSomethingWith(AnotherClass<? extends MyInterfacee> another) {
System.out.println(another.toString());
}
}
thanks.
I'm studying Java Generic type.
I have the abstract class AbstractInputdata.
public abstract class AbstractInputData {
....
}
Some class that extend AbstractInputData
public class Email extends AbstractInputData{
...
}
public class Mobile extends AbstractInputData{
...
}
......
A.
public class ProcessorA {
public static boolean isCustomData(AbstractInputData abstractInputData) {
....
}
}
B.
public class ProcessorB {
public static <T extends AbstractInputData> boolean isCustomData(T t) {
...
}
}
Is there any difference between A and B?
The only difference is that the second method with appear as a generic typed method via Reflections. It's behaviour will be the same except in odd cases like this
processorB.<MyType>isCustomData(t); // won't compile unless t is a MyType
You would have to tell it what type you expect it to match, which isn't that useful IMHO.
Since your methods only produce a boolean, there is no difference. But in case you want to return the input you can use B to preserve the generic type:
public class ProcessorB {
public static <T extends AbstractInputData> boolean isCustomData(T t) {
...
}
public static <T extends AbstractInputData> T copyCustomData(T t) {
...
}
}
ProcessorA could only return an object of type AbstractInputData while processorB returns Email or Mobile depending on the parameter type.
What I want is actually something like this:
public class Foo<T> {
// ...
}
public class Foo<T, S> {
// ...
}
Note that the name of the classes are the same, but the length of type list is different. The code above doesn't work in Java, but I hope it shows my intent. Is it possible to do similar thing in Java?
Example:
public class Foo<T> {
public Integer call(T input) {
// ...
}
}
public class Foo<T, S> {
public S call(T input) {
// ...
}
}
You don't override classes, you override methods. Classes might be subclassed.
This is possible:
public class Zoo<T, S, U> extends Foo<T>
{
// ...
}
I guess this is what you are trying to do:
Class foo1.Foo:
package foo1;
public class Foo<T, S> {
public S call(T input) {
// ...
}
}
Class foo2.Foo:
package foo2;
public class Foo<T> extends foo1.Foo<T, Integer> {
public Integer call(T input) {
// ...
}
}
When you are defining a class as
public class Foo<T> {
// ...
}
Technically we are telling the compiler to replace the Generic with a Object as T is unbounded.
When we define the class as
public class Foo<T, S> {
// ...}
Compiler will not know how to resolve this.