How to perform a checked cast? - java

I am new to Generics and am having an issue.
Consider the following code:
public class A {}
public class B extends A {}
public <T extends A> T getB()
{
A test = new B();
Class<B> clazz = B.class;
if (clazz.isInstance(test))
{
return (T)test;
}
return null;
}
This generates an Unchecked cast warning. on the return (T)test; line.
but clearly I am checking the type with the if (clazz.isInstance(test)) line.
Is there a way to do a "checked cast"?
I'm not looking to just suppress the warning but actually implement a checked cast. Unfortunately I can't find information on how to perform a checked cast.

Is there a way to do a "checked cast"?
Sure, although it's important to note that it doesn't really help you here, because your method is hard-coded to use B in a few places. You can perform the cast with:
clazz.cast(test)
... but that will cast to B, not T. In particular, suppose I ran:
public class C extends A {}
...
C c = foo.<C>getB();
How would you expect that to work?
You might want to change your code to something like:
public <T extends A> T getB(Class<T> clazz)
{
A test = // get A from somewhere
return clazz.isInstance(test) ? clazz.cast(test) : null;
}
Then that's fine, because clazz.cast will return a value of type T, which you're fine to return.

Related

Java generics and classcastexception

not sure whats wrong here, but haven't used generics for a while, so its probably something I don't remember, but basically this code is "supposed to" work as far as I'm concerned, yet it does not:
public static class MyModel {}
protected static <T extends MyModel> FlowableTransformer<Response<T>, T> convert(Class<T> responseClass) {
return observable -> observable.map((Function<Response<T>, T>) response -> {
MyModel m = new MyModel();
return (T)m;
});
}
When using this, I get a Classcastexception at runtime. Basic idea would be that the method could return with either a "MyModel" or any of its subclasses (so T extends MyModel). Yet it cannot return with MyModel itself now?
Obviously just the gist of the problem and not actual code. Any help is appreciated.
Suppose we have these 3 classes:
public class A {}
public class B extends A {}
public class C extends B {}
and the following function:
public static <T extends A> T foo() {
A ret_val = new A();
return (T)ret_val;
}
The following code will work correctly, without any exception:
A obj = foo();
because, here the ret_val inside foo() method is casted to A which is the actual type of ret_val.
But the following code will throw an exception:
B objB = foo(); // throws ClassCastException exception
C objC = foo(); // throws ClassCastException exception
because, what you are trying to do here, is casting an object of type A to B(or C), which is illegal.

Issue with class cast exception between subclasses and collections in Java

So, I have the following two classes:
Class A { }
Class B extends A { }
Class C extends A { }
And the following method:
public void foo(Collection<A> bar) {
List<A> listA = new ArrayList<>();
for(A a : bar) {
//a and anotherA are both of the same subtype of A
A anotherA = getAnotherA(a);
listA.add(anotherA);
}
bar.clear();
bar.addAll(listA);
}
Now, I am trying to call this method two separate ways, but I cannot get the casting to work properly... Hopefully I am just overlooking something small.
So, here are the two ways I am calling it:
Way 1:
A obj = ...;
Field field = //get field representing a collection of sub-type of A
Collection<A> collection = field.get(...);
foo(collection);
way 2:
B obj = ...;
Set<C> setOfC = b.getSetOfC();
foo(setOfC);
I have tried numerous casting attempts, but I cannot seem to get it to compile! For instance, in way 2, I tried casting setOfC to Set<A>, but I get a class cast exception. I have tried to cast bar to Collection<? extends A>, but then bar.addAll(..) fails. I have tried to add a generic to foo, but also get errors. In way 1, I have also tried to cast collection to Collection<? extends A>, but still not luck.
You cannot pass a Set<C> into a method expecting a Collection<A>, even though a Set is a Collection, and even though a C is an A, because Java's generics are invariant.
You can introduce a type parameter on the foo method, with an A upper bound. Then you can use the type parameter throughout the method. This will ensure that the same subtype of A is used.
public static <T extends A> void foo(Collection<T> bar) {
List<T> listA = new ArrayList<>();
for(T a : bar) {
//a and anotherA are both of the same subtype of A
T anotherA = getAnotherA(a);
listA.add(anotherA);
}
bar.clear();
bar.addAll(listA);
}
Your comment seems to indicate that a and anotherA are of the same type, so this should compile for you. If not, then the getAnotherA method will need some work so that passing in a C will return a C and not an A.
if you have this classes:
Class YourA { }
Class YourB extends YourA { }
Class YourC extends YourA { }
your method's signature most be like
public <G extends YourA> void foo(Collection<G> bar) {...}

Why argument type extending String can't be used like String

Supposing I have a code like this
class B<T extends String>
{
T f(T a){
return a+"def";
}
}
why does it give me a compile time error "error: incompatible types"? Why can't I use something that extends String like a string?
The + operator applied to a String and anything else is the String concatenation operator and produces a String.
Your return type is T, not String. T may (won't, but theoretically could) be any subtype of String. For that reason, this cannot be allowed to compile.
Similarly,
class Foo {}
class B<T extends Foo> {
T f(T a) {
return new Foo();
}
}
class Bar extends Foo {}
but
new B<Bar>().f(new Bar());
should have a return type of Bar, but would return a Foo. That doesn't make sense and so doesn't compile.
String is final, nothing can extend it, so your generic class' definition is not really useful. I'm surprised <T extends String> compiled at all.

Method return type to fulfill multiple interfaces - Again

Today I ran into the following problem. Consider the setup:
interface A {
void foo();
}
interface B {
void bar();
}
class Impl implements A,B {
public void foo() { }
public void bar() { }
}
class Usage {
void worksAsParameter(){
acceptIt(new Impl());
}
<T extends A & B> void acceptIt(T foo){
}
<T extends A & B> T returnIt(){
return new Impl(); // <-- Compile error
}
}
The code compiles except in the last statement as marked. Eclipse gives me the error: Type mismatch: cannot convert from Impl to T
My question is: Why is Impl assignable to T when given as a parameter (shown in worksAsParameter but not when T is a return type ?
And also, what expression aside from null will satisfy the type T in the case where Impl does not?
Please note that this question is not the same as this SO question although similar.
Edit: Fixed typo.
=== Summary ===
It would seem I had misunderstood how generic return types work. I will try to write up my new understanding of it.
Lets look at the issue:
<T extends A & B> T returnIt(){
return new Impl(); // <-- Compile error
}
My initial assumption was that the implementing class (in this case Usage) decided on the concrete type for T with the restriction that it must extend A and B. Apparently it is the caller/callsite that gets to decide what T is and Usage must supply a value that is assignable to T. However, as T is a compile time deal only it is impossible to supply such a value aside from null (as it is assignable to anything).
Afaik this means that any code of the form will only ever be able to return null:
<T extends A> T returnIt(){
return x; // <-- Compile error
}
A fairly unintuitive feature that is hopefully more useful in a different setting. Thanks Peter!
The reason
<T extends A & B> T returnIt(){
return new Impl();
}
doesn't compile is that T could be any class which extends A and B. You happen to know there is only one possible class at the moment, but the compile doesn't "know" this.
e.g.
class AB extends A, B { }
Usage usage = ...
AB ab = usage.<AB>returnIt(); // T is AB not Impl.
You can force the issue with
<T extends A & B> T returnIt(){
return (T) new Impl(); // unchecked cast warning.
}
but a better solution is
Impl returnIt(){
return new Impl();
}
This defines two generics
<T extends A, B>
T extends A and also B extends Object
What you may have intended is
<T extends A & B>
where T must extend A and B.
You can also do something like below to avoid compilation error.
<T extends A & B> T returnIt(Class<T> type) {
return type.cast(new Impl());
}

Java Generics: Multiple Inheritance in Bounded Type Parameters <T extends A & I>

I am about to create a factory which creates objects of a certain type T which extends a certain class A and another interface I. However, T must not be known. Here are the minimum declarations:
public class A { }
public interface I { }
This is the factory method:
public class F {
public static <T extends A & I> T newThing() { /*...*/ }
}
This compiles all fine.
When I try to use the method the following works fine:
A $a = F.newThing();
...while this does not:
I $i = F.newThing();
The compiler complains:
Bound mismatch: The generic method newThing() of type F is not applicable for the arguments (). The inferred type I&A is not a valid substitute for the bounded parameter
I can't understand why. It is clearly stated that "newThing returns something of a certain type T which does extend the class A and implement the interface I". When assigning to A everything works (since T extends A) but assigning to I does not (because of what?, clearly the thing returned is both an A and an I)
Also: When returning an object, say B of the type class B extends A implements I, I need to cast it to the return type T, although B matches the bounds:
<T extends A & I> T newThing() {
return (T) new B();
}
However, the compiler does not throw any warnings like UncheckedCast or the like.
Thus my question:
What is going wrong here?
Is there an easy away to achieve the desired behavior (i.e. assigning to a variable of static type A or I), like there is in solving the return-type-problem by casting, in the factory method?
Why does the assignment to A work, while to I does not?
--
EDIT: Here the complete code snippet which totally works using Eclipse 3.7, project set up for JDK 6:
public class F {
public static class A { }
public static interface I { }
private static class B extends A implements I { }
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
}
}
EDIT: Here is a complete example with methods and invocation which does work at runtime:
public class F {
public static class A {
int methodA() {
return 7;
}
}
public static interface I {
int methodI();
}
private static class B extends A implements I {
public int methodI() {
return 12;
}
}
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
System.out.println($a.methodA());
}
}
As for the second question:
Consider this case:
class B extends A implements I {}
class C extends A implements I {}
Now, the following uses type inference:
<T extends A & I> T newThing() {
return (T) new B();
}
So you could call this:
C c = F.newThing(); //T would be C here
You see that T could be anything that extends A and I you can't just return an instance of B. In the case above the cast could be written as (C)new B(). This would clearly result in an exception and thus the compiler issues a warning: Unchecked cast from B to T - unless you're supressing those warnings.
This doesn't do what you expect it to. T extends A & I indicates that the caller can specify any type that extends A and I, and you'll return it.
I think that one way to explain it is by replacing the type parameter with the actual type.
The parameterized signature of the methods is:
public static <T extends A & B> T newThing(){
return ...;
}
The <T extends A & B> is what is called a type parameter. The compiler would expect that this value is actually substituted with the actual type (called type argument) when you actually use it.
In the case of your method the actual type is decided by means of type inference. That is, <T extends A & B> should be replaced by a real existing type that extends A and implements B.
So, let's say that classes C and D both extends A and implements B, then if your signature were like this:
public static <T extends A & B> T newThing(T obj){
return obj;
}
Then, by type inference, your method would be evaluated as follows:
public static C newThing(C obj){
return obj;
}
if you invoke with newThing(new C()).
And would be as follows
public static D newThing(D obj){
return obj;
}
if you invoke with newThing(new D()).
This would compile just fine!
However, since you are not actually providing any kind of type to validate type inference in your method declaration, then the compiler could never be sure what is the actual type (type argument) of your type parameter <T extends A & B>.
You might expect that the actual type is C, but there may be thousands of different classes that satisfy that criteria. Which of those should the compiler use as the actual type of your type argument?
Let's say that C and D are two classes that extend A and implements B. Which of these two actual types should the compiler use as type argument for your method?
You could have even declared a type argument for which there is not even an existing type that you can use, like saying something that extends Serializable and Closable and Comparable and Appendable.
And perhaps there is not a class in the whole world that satisfies that.
As such, you must understand that the type parameter here is just a requirement for the compiler to validate the actual type that you use, a placeholder for the actual type; and this actual type must exist at the end and the compiler will use it to replace appearances of T. Therefore the actual type (type argument) must be inferable from the context.
Since the compiler cannot tell with certainty which is the actual type that you mean, basically because there is no way to determine that by type inference in this case, then you are forced to cast your type, to ensure the compiler that you know what you are doing.
As such, you could implement your method using type inference like this:
public static <T extends A & B> T newThing(Class<T> t) throws Exception{
return t.newInstance();
}
This way, you would be actually telling the compiler what is the actual type argument to be used.
Take into account that when the bytecodes are generated, the compiler must substitute T for a real type. There is no way to write method in Java like this
public static A & B newThing(){ return ... }
Right?
I hope I have explained myself! This is not simple to explain.
Simplest solution is create an abstract base class that extends and implements whatever class and interfaces you want and return that type. It doesn't matter that you're constraining your return type to extend this base class as you were already constraining the return type to its superclass.
eg.
class C {}
interface I {}
abstract class BaseClass extends C implements I {}
// ^-- this line should never change. All it is telling us that we have created a
// class that combines the methods of C and I, and that concrete sub classes will
// implement the abstract methods of C and I
class X extends BaseClass {}
class Y extends BaseClass {}
public class F {
public static BaseClass newThing() {
return new X();
}
public static void main(String[] args) {
C c = F.newThing();
I i = F.newThing();
}
}

Categories

Resources