Generics and varargs java - java

Below is my set up
public interface Test<T extends MyInterface>
{
someMethod(T... a)
}
public class TestImpl implements Test<MyInterfaceImpl>
{
someMethod(MyInterfaceImpl... a)
}
public class MyInterfaceImpl implements MyInterface {}
public someClass { #Autowired TestFactory testfactory
......
// getting an error -- Type mismatch Can't assign non-array value to an array
testfactory.getTest(Type type).someMethod(new MyInterfaceImpl())
}
public class TestFactoryImpl implements TestFactory { Test getTest(Type type) { return registry.get(type)}}
which in turn is results in java.lang.ClassCastException: [Lcom.test.MyInterface; cannot be cast to [Lcom.test.Impl.MyInterfaceImpl;
but the below works
testfactory.getTest(Type type).someMethod(new MyInterfaceImpl[]{new MyInterfaceImpl()})
Not sure what is happening. Help please

Ok .. the problem is within the design of your already existing code (which you can't change). Having public interface Test<T extends MyInterface> and then public class TestImpl implements Test<MyInterfaceImpl> is wrong.
TestImpl is implementing Test with MyInterfaceImpl whereas the original Test interface only expects an object that extends MyInterface and not implement it.
There will be a type confusion at runtime when executing the code. Not only does the following line throw a ClassCastException
test.someMethod(new MyInterfaceImpl());
but also test.someMethod(); by itself throws an exception. So let's say if you factory called this method passing no argument, you would still get an exception as the original designed is flawed. In a normal situation test.someMethod(); should not throw an exception to begin with. You will need to talk to the appropriate party to put a fix to this serious issue.
Below is a sample solution:
The method someMethod(MyInterface...) belongs to the raw type Test. References to generic type Test<T> should be parameterized.
This means you should Test<MyInterfaceImpl> test to avoid getting this error with only the new operator.
Test<MyInterfaceImpl> test
...
test.someMethod(new MyInterfaceImpl());
The above code will work with no problem.

A better solution is in your TestImpl class do the following
public class TestImpl implements Test<MyInterface>{...}
instead of
public class TestImpl implements Test<MyInterfaceImpl>{...}
That way you don't need to explicitly parameterize your object instance (i.e. you could just do
Test test
...
test.someMethod(new MyInterfaceImpl());
)

Related

Java generics incompatible types while subclassing

While sub classing from generic class type/Formal type parameter T/E with valid class type/Actual type parameter say e.g. Type/String there are many combinations occurs and that confusing which one to use and when?
public class SubClass<T> implements SuperIfc<T> <-- It is straight forward to understand
public class SubClass<T> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc<T>
public class SubClass<Type> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc
public class SubClass implements SuperIfc<Type>
public class SubClass implements SuperIfc<T> <--- Hope we cannot declare <T> in his case while initialising SubClass.
// Bounded type parameter
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T extends Type> implements SuperIfc<T> <-- Looks <T> at SuperIfc also refers <T extends Type>, and no need to declare it again at SuperIfc.
// Recursive type bound
public class SubClass<T extends Comparable<T>>> implements SuperIfc<T>
public class SubClass<T extends Comparable<T>>> implements SuperIfc<Type>
So that i can be more clearer on solving incompatible types while subclassing
Case_1:
public class Test {
interface TestIfc {
public static <T extends TestIfc> T of(int choice) {
if(choice == 1) {
return new TestImpl(); <-- PROB_1: incompatible type error
} else {
return new SomeOtherTestImpl(); //incompatible type error
}
}
}
static class TestImpl implements TestIfc {}
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc {
//The below method also having same error though with declaration
public T of() {
return new TestImpl(); <-- PROB_2: incompatible type error
}
}
}
Case_1: PROB_1: return type is T extends TestIfc and returned TestImpl implements TestIf So what is wrong?
Case_1: PROB_2: Similar to PROB_1, how to rectify without external casting. Please help.
Case_2:
public interface SuperIfc<T> {
public T create(Object label);
}
class Type {
public static Type of(){
return new Type();
}
}
------
public class SubClass<Type> implements SuperIfc<Type>{
#Override
public Type create() {
return Type.of(); <---- PROB_1: cannot resolve method
}
}
-------
public class SubClass<T extends Type> implements SuperIfc<Type>{
#Override
public Type create() {
return Type.of(); <---- PROB_1: is resolved
}
}
SuperIfc<Type> object = new SubClass(); <-- PROB_2 Unchecked assignement warning
SuperIfc<Type> object = new SubClass<TypeImpl>(); <-- PROB_3: bound should extend Type
I would like to know how to resolve Case_2, PROB_1 and PROB_2 together?
How to write subclass for generic super class with class types and what are the rules?
What should be taken care when changing generic T to class Type while subclassing? may be the difference between below and when to use?
public class SubClass<Type> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc
public class SubClass implements SuperIfc<Type>
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T extends Type> implements SuperIfc<T>
public class SubClass<T> implements SuperIfc<Type>
In the first of() method, the method can return any type that implements InformationIfc, but your method always returns a specific implementation - InformationImpl - which is not acceptable.
For example, if you had some other class SomeOtherInformationImpl that implements that interface, the caller of that method would be allowed to write:
SomeOtherInformationImpl i = InformationImpl.of();
but your method doesn't return a SomeOtherInformationImpl.
The second of() method has the same issue as the first method.
If you instantiated your class with:
InformationImpl i = new InformationImpl<SomeOtherInformationImpl>();
the of() method would have to return a SomeOtherInformationImpl, not a InformationImpl.
Problems with case one.
PROB_1: return type is T extends TestIfc
Why do you have a generic here at all?. Since you have a static method I can do.
TestIfc broken = TestIfc<SomeOtherImplementation>.of(0);
SomeOtherImplementation is not a TestImpl. This is broken by design. What you really want is.
public static TestIfc of(int choice)
Next.
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc {
TestIfc is not parameterized, SomeOtherTestImp is, but it is completely unrelated to the interface you're implementing. Not to mention, TestIfc has a static method of that has nothing to do with the interface.
If I had to guess, I would think you want.
interface TestIfc<T>{}
static class TestImpl implements TestIfc<TestImpl> {}
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc<T>{}
That is the best I could come up with, because it is unclear what you actually want to happen.
Your examples for question 3
public class SubClass<Type> implements SuperIfc<Type>
This is broken, because SubClass<Type> declares Type to be the name of a generic parameter. It puts no restriction on the type, hence you get the method not found error.
public class SubClass<Type> implements SuperIfc
Broken, makes a generic parameter named Type, has nothing to do with your raw type version of SuperIfc
public SubClass implements SuperIfc<Type>
This is good.
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T> implements SuperIfc<Type>
These are both good, but the T has no relation to the SuperIfc parameter, hence your implementation would be.
public Type create(Object label);
The first generic parameter says the name you're going to use through the class.
This is a long answer, but please read through it (at least case 1 and the very end, you can skip over problem 2's solution in case 2 if you wish)
Case 1
Problem 1:
Your problem here is that the compiler cannot prove that T is the same as TestImpl or SomeOtherTestImpl. What if there was another class, call it TestFoo, that implemented TestIfc? Then, if you called the method as TestIfc.<TestFoo>of(1), you would expect an object of type TestFoo, but you would instead get a TestImpl, which is wrong. The compiler doesn't want you doing that, so it throws an error. What you should do is just remove the generics, like so:
public static TestImpl of(int choice) {
if(choice == 1) {
return new TestImpl();
} else {
return new SomeOtherTestImpl();
}
}
And then you can safely call TestIfc.of.
Problem 2:
This is basically the same thing. The compiler can't prove that the type parameter T of SomeOtherTestImpl is the same as TestImpl. What if you have an object like this (where TestFoo does not extend TestImpl but implements TestIfc)?
SomeOtherTestImpl<TestFoo> testImpl = ...;
And then you try to call the of method like this:
TestFoo testFoo = testImpl.of();
Can you see the problem here? Your of method returns a TestImpl, but you expect it to return a TestFoo because T is TestFoo. The compiler stops you from doing that. Again, you should just get rid of the generics:
public TestImpl of() {
return new TestImpl(); //This works now
}
Case 2
Problem 1
This problem is caused simply because you named your type parameter the same thing as your class - Type. When you changed the name of your type parameter to, say, T, it'll work because now Type is the name of a class. Before, your type parameter Type was hiding the class Type.
However, again, there is no need for a type parameter, since, you can just do this, and it'll satisfy all constraints.
public class SubClass implements SuperIfc<Type> {
#Override
public Type create() {
return Type.of();
}
}
Problem 2:
The unchecked assignment is because you didn't provide type arguments to SubClass when you did
SuperIfc<Type> object = new SubClass();
This can be fixed by either explicitly giving Type to SubClass:
SuperIfc<Type> object = new SubClass<Type>();
or by putting in an empty diamond (which I much prefer). This second approach means that the compiler can infer by itself that by new Subclass<>(), you mean new Subclass<Type>().
SuperIfc<Type> object = new SubClass<>();
Why the compiler complains:
That constructor call (new Subclass()) is basically like calling a method that looks like public SubClass makeNewSubClass() {...}. In fact, let's replace that statement above with this one, which will cause the same warning.
SuperIfc<Type> object = makeNewSubClass();
SubClass normally takes 1 type parameter (call it T), but here, it's not given any type parameters. That means that T could be anything, any class that extends Type (because of the constraint SubClass<T extends Type>).
You might thinking that if T is always going to be a subclass of Type, it should work all right, because the type of object above is SuperIfc<Type>. Assume there's a class like this - class TypeFoo extends Type - and that the method makeNewSubClass actually returns an object of type SubClass<TypeFoo> (new SubClass<TypeFoo>()).
Because of this, you expect object to be SuperIfc<Type> but it's actually SubClass<TypeFoo>. You probably think that that's all right, because after all, TypeFoo is a subclass of Type, right? Well, Java's generics are actually invariant, which means that for the compiler, SubClass<TypeFoo> is not a subclass of SuperIfc<Type> - it thinks they're 2 completely unrelated types. Don't ask me - I have no idea why :). Even SubClass<TypeFoo> isn't considered the same as or a subclass of SubClass<Type>!
That's why the compiler emits a warning - because raw types (that's what you call it when you have new SubClass() without giving type arguments) could represent anything. As for why it emits a warning and not an error - generics were introduced in Java 5, so for code before that to compile, the compiler lets you off with just a warning.
Problem 3:
According to the error, the type argument you gave (TypeImpl) should extend Type. This is because you defined SubClass as class SubClass<T extends Type> .... Here, you are giving TypeImpl as an argument for T, and so TypeImpl must extend Type, so all you need to do is put do class TypeImpl extends Type to solve that particular error.
However, even after you do that, you'll get an "incompatible types" error because, as I said earlier when talking about Problem 2, SuperIfc<Type> is not considered a supertype of SubClass<TypeImpl> even if TypeImpl extends Type.
You can do this
SuperIfc<TypeImpl> object = new SubClass<TypeImpl>();
or you can do this
SuperIfc<? extends Type> object = new SubClass<TypeImpl>();
In the second solution, we lose the information of what exactly T is in SuperIfc<T>; all we know is that it extends Type. The benefit of the second one is that you can later reassign any SubClass object to it (which doesn't work with the first version because it only allows SubClass<TypeImpl>:
SuperIfc<? extends Type> object = new SubClass<TypeImpl>();
object = new SubClass<Type>(); //works great
Alternative solution for Case 2
What it seems like you want to do is return objects of different type depending on type parameters. That's not really possible to do, since type parameters are erased at runtime. There are workarounds, though.
Here's one way to do it with a functional interface. It involves no casting and ensures type safety.
//This is the functional interface. You can also use Supplier here, of course
interface Constructor<T> {
T create();
}
class SubClass<T extends Type> implements SuperIfc<T> {
public Constructor<T> ctor;
public SubClass(Constructor<T> ctor) {
this.ctor = ctor;
}
#Override
public T create(Object label) {
return ctor.create();
}
}
class Type {}
class TypeImpl extends Type{}
By using a Constructor object, you will know that the returned Type object will always be of the right type. This is very easy to apply to your existing code - you don't need to write your own ConstructorImpl classes or anything.
You can now write this, and all you need to add is a method reference to the constructor (Type::new).
SuperIfc<Type> object1 = new SubClass<>(Type::new);
SuperIfc<? extends Type> object2 = new SubClass<>(TypeImpl::new);
With lambdas, you can also write this in a slightly more verbose way:
SuperIfc<TypeImpl> object = new SubClass<>(() -> new TypeImpl());
---
SuperIfc<? extends Type> object = new SubClass<>(() -> new TypeImpl());

Passing Class objects in java

I have an Interface and 2 concrete class that implement that interface,
public interface ITemplate{}
public Template implements ITemoplate {}
public Template2 implements ITemplate {}
I have a method that takes in the Class object and instantiates it.
public addTemplate(Class<ITemplate> template){
pipe.add(template.newInstance())
}
The problem is that when I call that method, it throws a compile time error:
instance.addTemplate(Template.class)
Compile Time Error :
addTemplate(java.package.ITemplate.class) cannot be applied to addTemplate(java.package.Template.class)
Am I missing something, or is there a work around for this?
Class<ITemplate> will strictly accept the ITemplate.class
Class<? extends ITemplate> will accept any of the classes implementing ITemplate.class
try this method:
// works for all classes that inherit from ITemplate.
public addTemplate(Class< ? extends ITemplate> template){
pipe.add(template.newInstance())
}
instead of
// only accepts ITemplate Type Class (Strict Type).
public addTemplate(Class<ITemplate> template){
pipe.add(template.newInstance())
}
Here is an explanation: when you use Class<ITemplate> it is a strict type of class Itemplate. It will never take any other type argument other than ITemplate, because it is resolved at compile time only.
However Class <? extends ITemplate> can accept all objects that are either ITemplate or have ITemplate as a superclass.

Specify that a method argument must inherit from one class and implement an interface

I am writing a method which requires that one of its arguments descend from a particular class (MyClass) and implement an interface (MyInterface).
A crude way of doing this would be
public void doStuff(MyClass arg0) {
if (!(arg0 instanceof MyInterface))
throw new IllegalArgumentException("arg0 must implement MyInterface");
// do whatever we need to do
}
Note that MyClass does not implement MyInterface, and both are classes that I import as they are.
Is there a more elegant way of doing this, preferably one that would already flag errors at build time?
You can do that with the following generic method:
public <T extends MyClass & MyInterface> void doStuff(T arg) { ... }
Assuming the following classes (and interfaces)
class MyClass {}
interface MyInterface {}
class A extends MyClass {}
class B implements MyInterface {}
class C extends MyClass implements MyInterface {}
the following two statements are illegal (compiler error)
doStuff(new A());
doStuff(new B());
whereas the following statement will compile
doStuff(new C());
See JLS ยง4.4 (Type Variables) for more information about the somehwat weird type variable declaration.

ClassCastException when mocking generic type using Mockito

I have a abstract test class with a generic parameter.
public abstract class AbstractCarTest<G> {
...
#Mock
protected G carMock;
...
}
I've implemented a concrete test class of it.
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
}
The method signature looks like this
public Truck getTruck(String name);
When running TruckTest I get a ClassCastException saying
java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$... cannot be cast to com.example.Truck
Why is that? Any way I can work around that?
Mockito is generating your mock class at runtime. Mocks are created by subclassing a given class and by overriding all of its methods. (Thus the limitation of not mocking final classes or methods.) At runtime, all generic types are erased and replaced by their upper type bound, in your AbstractCarTest this is Object as you do not specify an explicit upper bound. Therefore, Mockito sees your raw class as:
public abstract class AbstractCarTest {
#Mock
protected Object carMock;
}
at runtime and will create a mock that extends Object instead of your desired Truck class. (You cannot see this because cglib has a bug where it cannot extend Object directly. Instead, Mockito is extending an internal class called ClassWithSuperclassToWorkAroundCglibBug.) Usually, the compiler would issue a type error at compile time where the generic type is still available but at runtime, you experience heap pollution instead.
A work around would be as follows:
public abstract class AbstractCarTest<T> {
protected abstract T getCarMock();
// define some base test cases here that use the generic type.
}
public class TruckTest extends AbstractCarTest<Truck> {
#Mock
private Truck carMock;
#Override
protected Truck getCarMock() { return carMock; }
// ...
when(truckFactory.getTruck(anyString()).return(getCarMock());
}
By defining your mocked type without using generics, you are able to access the mock from the abstract base class by a getter where the mocked type is correctly defined as Truck.
It seems like Mockito will not mock a class when it does not know it's concrete type.
You can get around the problem by doing the following:
public abstract class AbstractCarTest<G> {
...
protected G carMock;
...
#Before
public void setUp() throws Exception {
carMock= Mockito.mock(getClazz());
}
protected abstract Class<G> getClazz();
}
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
#Override
protected Class<Truck> getClazz() {
return Truck.class;
}
}
Just wanted to improve a bit on the answer from #geoand.
public abstract class AbstractCarTest<G> {
...
protected G carMock;
...
#Before
public void setUp() throws Exception {
carMock= Mockito.mock(getClazz());
}
private Class<G> getClazz() {
return (Class<G>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
}
This ends up removing the need for the abstract method. The index supplied to getActualTypeArguments() will depend on where the Type Argument appears in your declaration. In most cases this will be 0 however if you have something like this:
public abstract class AbstractCarTest<A, B, C, G>
And you wanted to use the Class of G you would need to supply and index of 3.

How to return a subclass of parameterized return type whose parametric type is a subclass of the return type's parametric type?

The title sounds a bit complicated, but in fact the question is quite simple.
I have the following method:
AbstractClassA<AbstractClassB> myMethod() {...}
And I want to invoke it like this:
ClassAImpl<ClassBImpl> result = myMethod();
The problem is that no matter what I tried, I ended up with unchecked cast warning or compiler error.
What is the right way of doing it?
It all depends. The simplest answer is that you're just going to need to cast the results of myMethod to the same type as result. Suppose we take all the generics out of the problem, like this:
public class Demo {
private abstract class AbstractClassA {}
private class ClassAImpl extends AbstractClassA {}
private abstract class AbstractClassB {}
private class ClassBImpl extends AbstractClassB {}
Demo() {
ClassAImpl result = myMethod();
}
public AbstractClassA myMethod() {
return new ClassAImpl();
}
public static void main(String[] args) {
new Demo();
}
}
The call to myMethod is still a compiler error, because the types don't match. A good way to think about this is to imagine that you don't know anything about the code in the body of myMethod. How would you know that it's returning a ClassAImpl and not some other subtype of AbstractClassA? So to make it work, you'd need to invoke myMethod like this:
ClassAImpl result = (ClassAImpl)myMethod()
As for the generics bit, you probably want your method signature to be something like:
public AbstractClassA<? extends AbstractClassB> myMethod()
but maybe want something like:
public AbstractClassA<ClassBImpl> myMethod()
or even:
public ClassAImpl<ClassBImpl> myMethod()
All except the last are going to require some explicit casting.
Why on earth would you do something like that anyway? Methinks either your design is wrong or you are leaving out some important information. If myMethod is in the same class as the attempted call, so that your caller is privy to information about how the method is implemented, you should create a private myMethodAux that returns the more specific type. You are probably still going to run into variance problems, however, as ClassAImpl<ClassBImpl> is not a subclass of AbstractClassA<AbstractClassB>. It is a subclass of AbstractClassA<? extends AbstractClassB>.
Your comment above seems to indicate that you were not aware of this variance restriction on the subtyping relation in Java.
You can't assign a super type into sub type without explicit type casting.
Use instanceof operator before down casting.
Try Class#isInstance()
abstract class AbstractClassA<T extends AbstractClassB> {}
abstract class AbstractClassB {}
class ClassAImpl<T extends AbstractClassB> extends AbstractClassA<T> {}
class ClassBImpl extends AbstractClassB {}
...
public static AbstractClassA<? extends AbstractClassB> myMethod() {
return new ClassAImpl<ClassBImpl>();
}
...
AbstractClassA<? extends AbstractClassB> returnValue=myMethod();
if(returnValue.getClass().isInstance(new ClassAImpl<ClassBImpl>())){
ClassAImpl<ClassBImpl> result=(ClassAImpl<ClassBImpl>)returnValue;
...
}

Categories

Resources