All! I have breaking my head over this things for a few hours now. I'm sorry if it's something so trivial but I guess I don't understand Java generics well enough. I'm a novice Java programmer.
I have 2 interfaces. Int1 and Int2. Int2 extends Int1. Int2Impl implements Int2. Lesson1.java and AnotherClass.java are given below also. Questions follow after the classes.
Int1.java
public interface Int1<E> {
public Lesson1<E> interfaceimpl(Class<E> klass);
}
Int2.java
public interface Int2<E> extends Int1<E> {
String getSomething();
}
Lesson1.java
public class Lesson1<E> {
}
Int2Impl.java
public class Int2Impl<E> implements Int2<E> {
Class<E> klass;
#Override
public String getSomething() {
return "nothing";
}
#Override
public Lesson1<E> interfaceimpl(Class<E> klass) {
this.klass = klass;
return null;
}
}
AnotherClass.java
public class AnotherClass<E> {
private Int2<E> interface2;
private <E> void newMethod(Class<E> klass) {
interface2 = new Int2Impl<>();
**interface2.interfaceimpl(klass);**
}
}
The line of code that's causing a compilation issue is,
interface2.interfaceimpl(klass); in the class AnotherClass.java
the errors and the quickfixes that Eclipse offers are:
Error:
The method interfaceimpl(java.lang.Class<E>) in the type Int1<E> is not
applicable for the arguments (java.lang.Class<E>)
Quick Fixes:
1) Change method interfaceImpl(Class<E>) to interface(Class<E>)
2) Cast Argument klass to Class<E>
3) Change type of klass to Class<E>
4) Create method interfaceImpl(Class<E>) in type 'Int2'
None of the quick fixes make sense to me. Plus they also don't fix the problem regardless of which one I choose. Can someone
point out the mistake and why Eclipse throws this error?
Thanks!
Your AnotherClass is already of generic type E. No need to define E again at method level.
Just remove <E> from your newMethod() as follows:
public class AnotherClass<E> {
private Int2<E> interface2;
private void newMethod(Class<E> klass) {
interface2 = new Int2Impl<>();
interface2.interfaceimpl(klass);
}
}
Related
package ir.openuniverse;
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
System.out.println(A.class.getField("t").getType().getName());
}
}
class A extends B<D> {}
class B<T extends C> {
public T t;
}
class C {}
class D extends C {}
The output is ir.openuniverse.C. Why? I expect D!
EDIT:
This question wasn't about workarounds or alternative ways. So answers aren't about workarounds. For alternative ways see myself answer below.
This happens because of type erasure.
Java compiles your generic class B<T> into byte code suitable for use with all classes that may reference it, including any class that may be extending B<T>.
Since T is restricted to classes extending C, Java knows that any value that you could assign B.t will extend C, so it compiles your class into an equivalent
class B {
C t;
}
At this point any assignment of t would work; reading from t would yield C, though, so the compiler must do some "magic" to fix this. Specifically, the compiler inserts type casts in places where the subtype is known. It may also generate bridge methods if necessary. See the link at the top of the answer for the details.
During compilation, Java's type erasure change
class B<T extends C> {
public T t;
}
to :
class B<C> {
public C t;
}
Since getType() identifies the declared type for the field, the output is ir.openuniverse.C
Thanks for all's helps. Finally, I forced to change my A's definition to:
class A extends B<D> {
public D t;
}
It's sufficient for my purpose (although I don't like it at all!).
EDIT:
Above way is not a fundamental way. See #DanielPryden's first two comments below.
Alternative workaround:
class A extends B<D> {
#Override public D getT() { return super.getT(); }
}
class B<T extends C> {
private T t;
public T getT() { return t; }
}
And in main():
System.out.println(A.class.getMethod("getT").getReturnType().getName());
Output: ir.openuniverse.D
Another way:
NOTE: This is not a solution to the main problem (See #DanielPryden's fourth comment below). But maybe helps you (like me).
This workaround can be used when you have at least one instance of A:
public class Main {
public static void main(String[] args) {
A a = new A();
System.out.println(a.t.getClass().getName());
// Or via reflection:
// System.out.println(a.getClass().getField("t").get(a).getClass().getName());
}
}
class A extends B<D> {
{ t = new D(); }
}
class B<T extends C> {
public T t;
}
Output: ir.openuniverse.D
In this case my IDE shows compilation error in return statement.
public class Base<T extends Base>{
public T get(){
return this;
}
}
When I add a typecast as in code bellow everything works fine, however I don't get why typecast is needed.
public class Base<T extends Base>{
public T get(){
return (T) this;
}
}
Doesn't Java replace all bounded generic occurrences with bounded type? Can someone explain what is going on under the hood and why typecast is needed?
Edit 1.
Thanks to Lothars and algrid answers it is now clear that this standalone case can cause ClassCastException. This is not safe so Base should probably be abstract.
The intent of this is to create a base class for Builder classes so that extended methods would return the type of the extending class. This is needed for method chaining. In the example bellow the return type of child.setParamOne(1) will be Child despite the fact that it is defined above in the inheritance hierarchy.
Is this code safe? Do you have any suggestions or alternatives for approaching this problem?
public abstract class Base<T extends Base>{
int paramOne;
public T setParamOne(int param){
this.paramOne = param;
return (T) this;
}
}
public final class Child extends Base<Child> {
int paramTwo;
public Child setParamTwo(int param){
this.paramTwo = param;
return this;
}
}
public static void main(String[] args) {
Child c = new Child()
.setParamOne(1)
.setParamTwo(1);
}
Why do you think that your this is of the type T? It's of the type Base<T>.
Try to run the following code and you'll get ClassCastException:
public class Main {
public static void main(String[] args) {
Base<Child> b = new Base<>();
// b.get() returns an instance of Base, not Child (however it's mistakenly cast to Child)
Child1 c = b.get();
}
public static class Base<T extends Base>{
public T get(){
return (T) this;
}
}
public static class Child extends Base {
}
}
The reason for this error is the same as the error being created for code like this:
public void myMethod(InputStream is) {
ByteArrayInputStream bais = is;
}
Just with generics. To get rid of the complier error you can do the cast as you did in your code:
public void myMethod(InputStream is) {
ByteArrayInputStream bais = (ByteArrayInputStream) is;
}
But this will fail during runtime if the passed inputstream is not a ByteArrayInputStream or a class derived from it. The same will happen with your code. Unless you only create instances of Base<Base> the cast will lead to an error when calling get.
In your example:
public class Base<T extends Base>{
public T get(){
return this;
}
}
the return statement is incorrect, because this is an instance of Base<T> and not T.
If your aim is to return the instance itself (by the way, I'm not sure why you would be doing this), the code should look like this:
public class Base<T extends Base>{
public Base<T> get(){
return this;
}
}
If your aim is to return the parameterized type, then you will probably not be able to do that. The parameterized type itself is not an instance within the Base class, but, again, just the parameterized type. If that is what you need, you can get the parameterized type class using reflection.
The conversion is unsafe because this (which has type Base<T>) may not be a T. We only know that T is a Base, but not the other way around.
There is no way to represent a "self type" in Java. So what you want to do is impossible. Instead, you can make an abstract method that forces implementing subclasses to provide a way to return a T:
public class Base<T> {
public abstract T get();
}
public final class Child extends Base<Child> {
public Child get() {
return this;
}
}
I searched stack overflow for this error, but none quite had the same design as I have. Suggestions for terminology to aid in finding a similar topic like the sample code below would be appreciated.
Here is a simple test case that demonstrates the error:
import java.util.List;
public class SimpleTest {
abstract class AbsTask<T>
{
}
abstract class AbsQueue<T extends AbsTask<?>>
{
private List<T> lst;
public void addSpecialItem()
{
lst.add(new SpecialItem()); // Error occurs here
}
}
class SpecialItem extends AbsTask<Void>
{
}
}
I am trying to add a method to my abstract class AbsQueue called addSpecialItem, which will insert the SpecialItem class into the list generic list T which is essentially a list of AbsTask.
Here is the error: The method add(T) in the type List<T> is not applicable for the arguments (SimpleTest.SpecialItem)
I can resolve this error if I type case the add line as follows:
lst.add((T)new SpecialItem());
Is there a way of handling this without type casting new SpecialItem() to T?
Your abstract class must be instantiated to define what T is. Try this:
public class SimpleTest {
static abstract class AbsTask<T> { }
static class AbsQueue<T extends AbsTask<?>> {
private List<T> lst;
public void addSpecialItem(T item) {
lst.add(item);
}
}
static class Test {
public void main() {
AbsQueue<SpecialItem> queue = new AbsQueue<SpecialItem>();
queue.addSpecialItem(new SpecialItem());
}
}
static class SpecialItem extends AbsTask<String> {
}
}
A List<T> is supposed to be a list that can only include elements of type T, but the code you've written doesn't ensure that SpecialItem is a subtype of T.
It's not clear what you actually want, but I think what you want is a List<AbsTask<?>>, not a List<T> for some specific T that extends AbsTask<?>.
At that line of code lst.add(new SpecialItem()); the compiler does not yet know what T is.
I have a (generic) class that holds meta data for other classes. The meta data is used in several ways (writing and reading XML data, database, output as text, etc). So far this works. But I have come across a problem when using all of this for classes that inherited from other classes.
Please have a look at the following code (I have tried to produce a minimal example that is compilabe except the line marked below):
class A {
public Meta<? extends A> getMeta() {
return new Meta<A>();
}
public void output() {
/*
* Error shown in eclipse for the next line:
* The method output(capture#1-of ? extends A) in the type
* Outputter<capture#1-of ? extends A> is not applicable for the arguments
* (A)
*/
getMeta().getOutputter().output(this);
}
}
class B extends A {
#Override
public Meta<? extends B> getMeta() {
return new Meta<B>();
}
}
class Meta<CLS> {
public Outputter<CLS> getOutputter() {
return null;
}
}
class Outputter<CLS> {
public void output(CLS obj) {
}
}
I can change A.getMeta() to return Meta<A> to make above line compilabe, but then I cannot override it as Meta<B> getMeta() in class B.
Any ideas on how to solve this?
What if you do this? It requires one more class, but it seems it is going to work:
class T{
//put common methods here, generic methods are not common, so they will not be here
}
class A extends T{
public Meta<A> getMeta() {
return new Meta<A>();
}
public void output() {
/*
* Error shown in eclipse for the next line:
* The method output(capture#1-of ? extends A) in the type
* Outputter<capture#1-of ? extends A> is not applicable for the arguments
* (A)
*/
getMeta().getOutputter().output(this);
}
}
class B extends T {
public Meta<B> getMeta() {
return new Meta<B>();
}
}
class Meta<CLS> {
public Outputter<CLS> getOutputter() {
return null;
}
}
class Outputter<CLS> {
public void output(CLS obj) {
}
}
if you do not want to create another method you can use composite. There are many good discussions about compositions over inheritance.
Everything will be the same except A and B classes:
class A{
public Meta<A> getMeta() {
return new Meta<A>();
}
...
}
class B {
private class A a;
public Meta<B> getMeta() {
return new Meta<B>();
}
//use a here if you need, a is composed into B
}
The reason you cannot override public Meta<A> getMeta() with public Meta<B> getMeta() is that instances of B will be castable to A, and such a casted instance would need to return a Meta<A>. While it may be that a Meta<B> can serve as a Meta<A>, the compiler doesn't know that.
Imagine instead that you are returning List<A> and a List<B>. It is allowable to put instances of A and B into a List<B>, but it is not allowable to put instances of B into a List<A>, so the List<B> that is actually being returned by B can not serve as a List<A>.
Changing List<A> to List<? extends A> allows the code to compile, because List<B> is technically a subclass of List<? extends A>, but it will not allow you to do everything you may expect.
B b = new B();
A casted = (A)b;
casted.getList().add(new A());
The compiler will accept the first and second line without issue, but it will take issue with the third:
The method add(capture#1-of ? extends A) in the type List<capture#1-of ? extends A> is not applicable for the arguments (A)
If you investigate a bit, you'll find that this casted variable will accept neither elements of A nor B. The compiler has remembered that the object was casted and may not actually be able to accept anything that extends A.
I'm trying to hunt down documentation for this behavior, but I'm failing. Eclipse tooltips are suggesting that I should give it an element of type null, which is obviously nonsense. I'll update if I find anything on it.
EDIT: The behavior described is a product of "Capture Conversion" as described here. Capture Conversion allows wildcards to be more useful by changing the bounds of type arguments over the course of assignments and casts. What happens in our code is simply that the bounds are constricted to the null type.
I will answer this myself since I found a working solution.
Although this solution is not type-safe, it works and requires the least changes to my existing codebase. If anyone comes up with something that works and doesn't require the #SuppressWarnings, I will accept that answer.
class A {
Meta<?> getMeta() {
return new Meta<A>();
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public void output() {
Outputter out = getMeta().getOutputter();
out.output(this);
}
}
class B extends A {
#Override
public Meta<?> getMeta() {
return new Meta<B>();
}
}
class Meta<CLS> {
public Outputter<CLS> getOutputter() {
return null;
}
}
class Outputter<CLS> {
public void output(CLS obj) {
}
}
Consider the following simple code
import java.util.*;
public class MainTest<T extends Object1<?,?>> {
List<T> list;
public MainTest(List<T> l) {
this.list=l;
}
public int testCompare() {
// fails to compile here
return list.get(0).compareTo(list.get(1));
}
public static void main(String[]args) {
List<Object1Impl> list = new ArrayList<Object1Impl>();
list.add(new Object1Impl());
list.add(new Object1Impl());
MainTest<Object1Impl> test = new MainTest<Object1Impl>(list);
System.out.println(test.testCompare());
}
}
interface Object1<E, V> extends Comparable<Object1<E,V>> { }
class Object1Impl implements Object1<Integer, Integer>{
public int compareTo(Object1<Integer, Integer> o) { return 0; }
}
I am aware that in this case the program will not compile (fails at testCompare() because T is extending unbounded Object1<?,?>). Is there any alternative to fix this besides making MainTest<T extends Object1<E,V>,E,V>?
EDIT: the error message is
The method compareTo(Object1<capture#1-of ?,capture#2-of ?>) in the type Comparable<Object1<capture#1-of ?,capture#2-of ?>> is not applicable for the arguments (T)
I have read Effective Java book but still can't really think of a solution..
Also, why is it that if I change interface Object1 into an abstract class the program will compile without any problem? This really puzzles me...
EDIT: when I mean changing into abstract class is as follows
abstract class Object1<E, V> implements Comparable<Object1<E,V>>{
public int compareTo(Object1<E,V> o) { return 0; }
}
class Object1Impl extends Object1<Integer, Integer>{ }
this will work (only if using Eclipse, compiling it manually using javac does not work) but I have no idea why
This is correct; the compiler has no way to verify that list.get(0) and list.get(1) are of the same type; one might be Object1<String, Integer> and the other Object1<BigDecimal, Double>.
To make sure that they are of the same type, you would have to bind those types:
public class MainTest<A,B,T extends Object1<A,B>> {
List<T> list;
public MainTest(List<T> l) {
this.list=l;
}
public int testCompare() {
// fails to compile here
return list.get(0).compareTo(list.get(1));
}
public static void main(String[]args) {
List<Object1Impl> list = new ArrayList<Object1Impl>();
list.add(new Object1Impl());
list.add(new Object1Impl());
MainTest<Integer, Integer, Object1Impl> test = new MainTest<Integer, Integer, Object1Impl>(list);
System.out.println(test.testCompare());
}
}
As far as I know, Java doesn't allow binding parameter types to classes without specifically specifying them.