How to load a custom class inherited from existing one? - java

I'm trying to create an object of a class, using just a name of this class:
public interface Foo {
}
public class Bar implements Foo {
}
[...]
Class<Foo> c = Class.forName("com.XXX.Bar").asSubclass(Foo.class);
Foo foo = c.newInstance();
Compiler says:
incompatible types found :
java.lang.Class<capture#47 of ? extends com.XXX.Foo>
required: java.lang.Class<com.XXX.Foo>
What's wrong here?

Since c is a some class which extends Foo, you should express it in the code using <? extends ...> syntax:
Class<? extends Foo> c = Class.forName("com.XXX.Bar").asSubclass(Foo.class);

Related

Java generics with multiple enums implementing an interface

I want to pass a list of enum classes to a method, where all of the enums implement a common interface, and have the method return one of the enum values.
Looking at Java Generics Wildcarding With Multiple Classes, it seems that
public class Main
{
interface Foo {}
enum First implements Foo {
A, B, C;
}
enum Second implements Foo {
X, Y, Z;
}
interface Bar {}
enum Third implements Bar {
M, N, P;
}
enum Fourth implements Bar {
A, X, Z;
}
public static <I, T extends Enum<?> & I>
I enumVarArgs(Class<? extends T>... classes)
{
// Do stuff and return some instance of T
return null;
}
public static void main(String[] args) {
Foo foo = enumVarArgs(First.class,
Second.class);
Bar bar = enumVarArgs(Third.class,
Fourth.class);
}
}
should do what I want. However, this fails to compile under Java 10:
[ERROR] /me/test/src/main/java/test/Main.java:[17,42] error: unexpected type
required: class
found: type parameter I
where I,T are type-variables:
I extends Object declared in method <I,T>enumVarArgs(Class<? extends T>...)
T extends Enum<?>,I declared in method <I,T>enumVarArgs(Class<? extends T>...)
[INFO] 1 error
From the error message, I am guessing that Java wants me to do something like T extends Enum<?> & Serializable, where I pass an actual interface, rather than a type parameter. However, I need the API to be general so that I remains a generic parameter.
Is there a syntax that makes this work?
If it matters, we are using Java 10.
If interfaces are your design, then you can make the two interfaces extend a marker interface:
interface FooBar{}
interface Foo extends FooBar {}
interface Bar extends FooBar {}
And you can use this marker interface:
public static <T extends Enum<?> & FooBar>
FooBar enumVarArgs(Class<? extends T>... classes) {
return null;
}
I think what you need is:
interface OnlyImpelents<T> {}
interface Foo extends OnlyImpelents<Foo> {}
interface Bar extends OnlyImpelents<Bar> {}
static <T extends Enum<?> & OnlyImpelents<? super T>> T enumVarArgs(T... values) {
return values[0];
}
public static void main(String[] args) {
Foo foo = enumVarArgs(First.A, Second.X);
}
The marker interface OnlyImpelents<T> prevents any class from implementing both Foo and Bar
The only way to get enum values from a enum class is via reflection, which doesn't need to have a static type. Personally I think this is not a good object-oriented design, using T... as args should be better

How to cast generic Class type into an another generic Class type in Java

This is what I want:
interface A {}
abstract class B implements A {}
class C extends B { /*few fields*/ }
class D extends B { /*few different fields*/ }
public void doSomething(Class<A> clazz) {
if(clazz.isAssignableFrom(B)) {
doSomethingElse((Class<? extends B>)clazz); // casting is good, but cannot pass this way to doSomethingElse
}
}
public <T extends B> void doSomethingElse(Class<T> clazz) {
if(clazz.isAssignableFrom(C)) {
// do something with C fields
} else if(clazz.isAssignableFrom(D)) {
// do something with D fields
}
}
But obviously I cannot cast the class type this way.
Is there a way to do it? I am very curious...
Thanks!
First of all, a value of type Class<A> can only be the class object for the exact class A, it cannot be any other class. The class object for some other class B would be Class<B>, and Class<B> is never a subtype of Class<A>. You probably want a Class<? extends A> parameter instead.
Second, your use of .isAssignableFrom() seems backwards (besides the fact that it's syntactically invalid as B is not a valid expression). clazz.isAssignableFrom(B.class) means that the class represented by clazz is a superclass of B, in which case it makes no sense to cast clazz to Class<? extends B>. You probably want B.class.isAssignableFrom(clazz) instead.
Once you make the first change above (make clazz type Class<? extends A>), the cast will compile, although it will be an unchecked cast. If you don't want an unchecked cast, instead of (Class<? extends B>)clazz, you can do clazz.asSubclass(B.class).

Java generics - Class<? extends Base>

I'm confused a bit about Java generics.
I have a class Test which extends from Base:
public class Test extends Base {
private final static Test obj = new Test();
new Auto(obj);
}
The constructor of Auto is like this (not my class):
public Auto (Class<? extends Base> programclass) {}
And I'm getting this error:
Test cannot be converted to Class<? extends Base>
How should I declare obj variable so I can pass it to Auto constructor?
Thanks

Can't add children.class to set of parent.class

I am trying to add the class object of a children class to a set of parent class object :
public class Main {
public static void main(String[] args) {
Set<Class<? extends A<?>>> set = new HashSet<>();
set.add(C.class); //this does not work
}
public abstract class A<T> {
}
public abstract class B<T, V> extends A<T> {
}
// Set<T> could be any other class, it is for demonstration purpose.
public class C<T> extends B<Set<T>, Set<T>> {
}
}
I get the following error :
The method add(Class<? extends Main.A<?>>) in the type Set<Class<? extends Main.A<?>>> is not applicable for the arguments (Class<Main.C>)
If I remove the '?' from the A, the code compiles, but I don't understand why. Can someone explain me why the "add" is not working ?
class is a 'class literal' which only looks like a static field.
As per definition, the type of C.class is Class<C>, where C is the name of a class.
So you cannot add it to a Set of Class<C<?>> (or in your case Set<Class<? extends A<?>>>)
For further information read 15.8.2 Class Literals in the Java specs:
http://docs.oracle.com/javase/specs/jls/se8/jls8.pdf

Java generics: <B extends BaseB> does not match <? extends BaseB>

I have two isomorphic type hierarchies. The base type of the first one is BaseA and the base type of the second one is BaseB. I know how to transform any object of any subclass of BaseB to its corresponding subtype of BaseA. I want to implement a method which takes object of type BaseB determines its class and constructs an object of the corresponding subtype of BaseA. Example code:
public interface BaseA...
public interface BaseB...
public class DerA implements BaseA...
public class DerB implements BaseB...
...
public interface Transform<A,B> {
A toA (B b);
}
public class DerAtoDerB implements Transform<DerA,DerB> {
DerA toA (DerB b){...}
}
public class Transformations {
private static Map<Class<?>, Transform<? extends BaseA, ? extends BaseB>> _map =
new HashMap<>();
static {
_map.put(DerB.class, new DerAtoDerB());
}
public static <B extends BaseB> BaseA transform(B b){
Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass());
return t.toA(b); // Compile error: Transform<A,B#2> cannot be applied to given types
}
Why <B extends BaseB> is not compatible with <? extends BaseB> ? Also if I try implementing the static transform method like this:
public static BaseA transform(BaseB b){
Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass());
return t.toA(b); // Compile error: Transform<A,B> cannot be applied to given types
}
I get a compilation error: Transform<A,B> cannot be applied to given types
Can anyone explain me what I am doing wrong with Generics?
The problem is that in the transform method the compiler can't know that the type parameter B extends BaseB and the second type parameter in the Transform class (? extends BaseB) that was gotten from the map actually represent the same subclass of BaseB. Nothing stops you from storing an incompatible type in the map:
_map.put(DerB.class, new AnotherDerAtoAnotherDerB()); // the types don't match
You are the one who guarantees that the types in the map match, so you need to tell the compiler by casting it to the correct type:
#SuppressWarnings("unchecked")
public static <B extends BaseB> BaseA transform(B b) {
Transform<? extends BaseA, B> t =
(Transform<? extends BaseA, B>)_map.get(b.getClass());
return t.toA(b);
}
When the compiler encounters a variable with a wildcard in its type it knows that there must have been some T that matches what was sent in. It does not know what type T represents, but it can create a placeholder for that type to refer to the type that T must be. That placeholder is called the capture of that particular wildcard.
I don't know why the compiler can't figure out that capture<? extends BaseB> could be capture<?> extends BaseB, maybe something with type erasure?
I would instead implement it like this:
interface BaseA {}
interface BaseB {}
class DerA implements BaseA {}
class DerB implements BaseB {}
interface Transform {
BaseA toA(BaseB b);
}
class DerAtoDerB implements Transform {
public BaseA toA(BaseB b) { return new DerA(); }
}
class Transformations {
private static Map<Class<?>, Transform> _map =
new HashMap<>();
static {
_map.put(DerB.class, new DerAtoDerB());
}
public static<B extends BaseB> BaseA transform(B b) {
Transform t = _map.get(b.getClass());
return t.toA(b);
}
}
? means unknown type.
When a variable is of type X you can assign it a value of type X or any subtype of X but "? extends X" means something else.
It means there is an unknown type that may be X or any subtype of X. It is not the same thing.
Example:
public static Transform<? extends BaseA, ? extends BaseB> getSomething(){
// My custom method
return new Transform<MySubclassOfA, MySubclassOfB>(); // <-- It does not accept BaseB, only MySubclassOfB
}
public static BaseA transform(BaseB b){
Transform<? extends BaseA, ? extends BaseB> t = getSomething();
return t.toA(b); // <--- THIS IS WRONG, it cannot accept any BaseB, only MySubclassOfB
}
In the example the compiler does not know if t admits any BaseB or what but I shown an example where it doesn't.
This thing compiles:
package com.test;
import java.util.HashMap;
import java.util.Map;
interface BaseA{}
interface BaseB{}
class DerA implements BaseA{}
class DerB implements BaseB{}
interface Transform<A,B> {
A toA (B b);
}
class DerAtoDerB implements Transform<BaseA,BaseB> {
public DerA toA(DerB b){ return null; }
#Override
public BaseA toA(BaseB baseB) {
return null;
}
}
public class Transformations {
private static Map<Class<?>, Transform<? extends BaseA, ? super BaseB>> _map = new HashMap<Class<?>, Transform<? extends BaseA, ? super BaseB>>();
static {
_map.put(DerB.class, new DerAtoDerB());
}
public static <B extends BaseB> BaseA transform(B b){
Transform<? extends BaseA, ? super BaseB> t = _map.get(b.getClass());
return t.toA(b);
}
}
The changes I made to your code are the following:
DerAtoDerB now implements Transform<BaseA,BaseB>, instead of Transform<DerA,DerB>
Type of second generic parameter of Map has changed to Transform<? extends BaseA, ? super BaseB> - pay attention to use of super instead of extends - it's the opposite type bound.
Main concept of Java generics: if ChildClass extends ParentClass it DOES NOT mean YourApi<ChildClass> extends YourApi<ParentClass>. E.g.:
NumberTransform<String, ? extends Number> intTransform = new IntegerTransform<String, Integer>(); // work with Integer numbers only
NumberTransform<String, ? extends Number> longTransform = new LongTransform<String, Long>(); // work with Long numbers only
longTransform.toA((Integer) 1); // you are trying to make this and got compilation error.
To help compiler replace your t initialization:
Transform<? extends BaseA, B> t = (Transform<? extends BaseA, B>) _map.get(b.getClass());

Categories