Problem with unbounded Java Generic interface - java

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.

Related

Java Generics issue with using a parameterized class

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);
}
}

Why doesn't type inference work in this case

I want to write a comparator which sorts objects having a parameter of type Number. I created a generic dto as follows
public class GenericSortingDTO<T extends Number> {
private T parameter;
public T getParameter() {
return parameter;
}
public void setParameter(T parameter) {
this.parameter = parameter;
}
}
I created a comparator like this
import java.math.BigDecimal;
import java.util.Comparator;
public class WeightComparator implements Comparator<GenericSortingDTO<Number>> {
#Override
public int compare(GenericSortingDTO<Number> o1,
GenericSortingDTO<Number> o2) {
return new BigDecimal(o1.getParameter().toString()).compareTo(new BigDecimal(o2.getParameter().toString()));
}
}
Now, I try to create an instance of an integer comparator
Comparator<GenericSortingDTO<Integer>> genericComparator = new WeightComparator();
But I get an error: Type mismatch: cannot convert from WeightComparator to Comparator<GenericSortingDTO<Integer>>
But it doesn't give a compilation error when I do this:
Comparator<GenericSortingDTO<Number>> genericComparator = new WeightComparator();
Can somebody explain this behaviour?
GenericSortingDTO<Integer>
and
GenericSortingDTO<Number>
are two disparate types. There is no hiererachical relationship between them. By implication the same is true of
Comparator<GenericSortingDTO<Integer>>
and
Comparator<GenericSortingDTO<Number>>
These two types are not assignment-compatible, therefore the compiler error.
However, there is no need to restrict your WeightComparator as you have. You are allowed to declare
public class WeightComparator implements Comparator<GenericSortingDTO<? extends Number>> {
#Override public int compare(GenericSortingDTO<? extends Number> o1, GenericSortingDTO<? extends Number> o2) {
return ...;
}
}
And since Numbers are comparable on their own, you'll be able to provide a meaningful implementation.
It would be simpler to declare your class like this:
public static class GenericSortingDTO<T extends Number>
implements Comparator<GenericSortingDTO<T>> {
//your current code here
#Override
public int compare(GenericSortingDTO<T> o1, GenericSortingDTO<T> o2) {
return new BigDecimal(o1.getParameter().toString()).compareTo(new BigDecimal(o2.getParameter().toString()));
}
}

The method add(T) in the type List<T> is not applicable for the arguments

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.

What method signature is appropiate returning a generic object?

What should be the signature of a method that takes a generic object and returns another generic object, one that either is the same or a sub class of the original class? That is, if the method takes some generic class A, the returned object is guaranteed to be either A or B such that B extends A (directly or indirectly)?
The code below exemplifies what I'm trying to do, in the function getList():
package com.company;
import java.util.ArrayList;
public class Main {
private Main(){
List<String> stringList = new GenericMessageListCreator.getList(StringGenericMessage.class);
}
private class GenericMessageListCreator() {
public List<GenericMessage<T1>> getList(Class<T1 extends GenericMessage> clazz) {
return new ArrayList<T1>();
}
}
private class GenericMessage<T> {
public GenericMessage(){};
private T internalValue;
public void setValue(T value) {
this.internalValue = value;
}
public void echoValue() {
System.out.println("I contain " + internalValue);
}
}
private class StringMessage extends GenericMessage<String>{}
private class IntegerMessage extends GenericMessage<Integer>{}
}
Example aside, in actuality I'm writing a registry of classes that are used for Commands in a command pattern. When I get an object by its class I want to fetch the appropriate Command and pass the object to it.
I think you are looking for this signature:
public <T1 extends GenericMessage> List<GenericMessage<T1>> getList(Class<T1> clazz) {
return new ArrayList<T1>();
}
You'll find more info about generic methods here.
EDIT
Based on what I understand from your sample code, I would go for something like (I corrected some syntax errors in your code):
private class GenericMessageListCreator {
public <U, V extends GenericMessage<U>> List<U> getList(Class<V> clazz){
return new ArrayList<U>();
}
}
private class GenericMessage<T> {
public GenericMessage(){};
private T internalValue;
public void setValue(T value)
{
this.internalValue = value;
}
public void echoValue() {
System.out.println("I contain " + internalValue);
}
}
private class StringMessage extends GenericMessage<String>{}
private class IntegerMessage extends GenericMessage<Integer>{}
Thus, you'll be able to create a List<String from `StringMessage like this:
List<String> stringList = new GenericMessageListCreator().getList(StringMessage.class);
I'm not even sure which method you want to have this behavious on, but I've assuming it's getList():
private class GenericMessageListCreator() {
public <T extends GenericMessage<?>> List<T> getList(Class<T> clazz) {
return new ArrayList<T>();
}
}

Generic instance variable in non-generic class

I'm trying to write a class that has a generic member variable but is not, itself, generic. Specifically, I want to say that I have an List of values of "some type that implements comparable to itself", so that I can call sort on that list... I hope that makes sense.
The end result of what I'm trying to do is to create a class such that I can create an instance of said class with an array of (any given type) and have it generate a string representation for that list. In the real code, I also pass in the class of the types I'm passing in:
String s = new MyClass(Integer.class, 1,2,3).asString();
assertEquals("1 or 2 or 3", s);
String s = new MyClass(String.class, "c", "b", "a").asString();
assertEquals("\"a\" or \"b\" or \"c\"", s);
Originally I didn't even want to pass in the class, I just wanted to pass in the values and have the code examine the resulting array to pick out the class of the values... but that was giving me troubles too.
The following is the code I have, but I can't come up with the right mojo to put for the variable type.
public class MyClass {
// This doesn't work as T isn't defined
final List<T extends Comparable<? super T>> values;
public <T extends Comparable<? super T>> MyClass (T... values) {
this.values = new ArrayList<T>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable<? super T>> List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}
error on variable declaration line:
Syntax error on token "extends", , expected
Any help would be very much appreciated.
Edit: updated code to use List instead of array, because I'm not sure it can be done with arrays.
#Mark: From everything I've read, I really want to say "T is a type that is comparable to itself", not just "T is a type that is comparable". That being said, the following code doesn't work either:
public class MyClass {
// This doesn't work
final List<? extends Comparable> values;
public <T extends Comparable> MyClass (T... values) {
this.values = new ArrayList<T>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable> List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}
error on add line:
The method add(capture#2-of ? extends Comparable) in the type List<capture#2-of ? extends Comparable> is not applicable for the arguments (T)
error on sort line:
Type mismatch: cannot convert from List<capture#4-of ? extends Comparable> to List<T>
Conclusion:
What it comes down to, it appears, is that Java can't quite handle what I want to do. The problem is because what I'm trying to say is:
I want a list of items that are
comparable against themselves, and I
create the whole list at once from the
data passed in at creation.
However, Java sees that I have that list and can't nail down that all the information for my situation is available at compile time, since I could try to add things to the list later and, due to type erasure, it can't guarantee that safety. It's not really possible to communicate to Java the conditions involved in my situation without applying the generic type to the class.
I think that the simple answer is that you cannot do that. If the type of one of a classes attributes depends on a type parameter, that parameter has to be declared at the class level. And I don't think that it "makes sense" any other way.
If T in your example is not a type parameter of the class, what is it? It cannot be the type parameter of the method, because that type is determined by how the method is called. (If the method is called in different static contexts with different inferred types for T, what is the notional type of T in the context of the attribute declaration?)
So to bring this back to what you are trying to do here, an instance of MyClass will hold elements of some type, and you want to be able to insert and remove elements in a statically typesafe fashion. But at the same time you don't want to be able to say what that type is. So how is the compiler supposed to statically distinguish between a MyClass instance that holds (say) Integer objects and one that holds String objects?
I don't even think you could implement this with explicit dynamic typechecks. (I think that type erasure means that the implementation of the getSortedList() method cannot find out what actual type is bound to its return type.)
No. The real solution is to make MyClass a generic class that declares the type parameter T; e.g.
public class MyClass <T extends Comparable<T>> {
and remove the declaration of the method-level type parameter T from the two methods.
There's plenty of unchecked warnings in this, but in principle it's not necessary to keep the List as anything but something containing things you know are Comparable. You enforce the rules you need to in the constructor, and everything else should be fine. How about something like this:
public class MyClass {
final private List<Comparable> values;
public <T extends Comparable<? super T>>MyClass(T... values){
this.values = new ArrayList<Comparable>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable<? super T>> List<T> getSortedLst() {
Collections.sort(this.values);
return (List<T>)this.values;
}
}
A quick test using the following shows that for classes that implement Comparable (like Integer and String) MyClass behaves as expected, but will throw a compilation error for classes that do not implement Comparable:
class Junk { }
public static void main(String[] args){
MyClass s = new MyClass(1,2,3);
System.out.println(s.getSortedLst());
MyClass a = new MyClass("c", "a", "b");
System.out.println(a.getSortedLst());
MyClass c = new MyClass(new Junk());
}
I believe the following will achieve what you want (stronger typing of Comparable). This will prevent people adding Comparable objects which are not from your interface to the list and allow multiple implementations.
public class test<T extends ComparableType> {
final List<T> values = new ArrayList<T>();
public test (T... values) {
for(T item : values) {
this.values.add(item);
}
}
public List<T> getSortedLst() {
Collections.sort(this.values);
return Collections.unmodifiableList(this.values);
}
}
public interface ComparableType extends Comparable<ComparableType> {}
public class ConcreteComparableA implements ComparableType {
#Override
public int compareTo(ComparableType o) {
return 0;
}
}
public class ConcreteComparableB implements ComparableType {
#Override
public int compareTo(ComparableType o) {
return 0;
}
}
edit:
I know this may be obvious; but if you do not wish the class to be Generic this solution will also work with:
public class test {
final List<ComparableType> values = new ArrayList<ComparableType>();
public test (ComparableType... values) {
for(ComparableType item : values) {
this.values.add(item);
}
}
public List<ComparableType> getSortedLst() {
Collections.sort(this.values);
return Collections.unmodifiableList(this.values);
}
}
Consider it like this (what I am about to say isn't reality. but it illustrates why you need to do what you need to do):
class Foo<T>
{
private T value;
T getValue() { return value; }
void setValue(T val) {value = val; }
}
// some code that uses the above class
Foo<Integer> iFoo = new Foo<Integer>();
Foo<String> sFoo = new Foo<String>();
iFoo.setValue(5);
sFoo.setValue("Hello");
When this happens the compiler (DOES NOT REALLY DO WHAT I AM ABOUT TO SAY!) generates the following code:
class IntegerFoo
{
private Integer value;
Integer getValue() { return value; }
void setValue(Integer val) {value = val; }
}
class StringFoo
{
private String value;
String getValue() { return value; }
void setValue(String val) {value = val; }
}
// some code that uses the above class
IntegerFoo iFoo = new IntegerFoo();
StringFoo< sFoo = new StringFoo();
iFoo.setValue(5);
sFoo.setValue("Hello");
If you were able to have the instance variables/methods parameterized without parameterizing the class the above thing (WHICH IS NOT REALITY!) wouldn't work.
What you are trying to do should be possible with static methods, but I don't think that is what you want.
Can you explain why you want to do the code you are trying to do? Perhaps we can figure out a better way to do what you want to do that works within the language.
I'd do it this way (I did it as a list or as an array), unless you really need the instance variable/methods:
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class MyClass
{
public static <T extends Comparable<T>> List<T> asSortedList(final T ... vals)
{
final List<T> temp;
temp = new ArrayList<T>(vals.length);
temp.addAll(Arrays.asList(vals));
Collections.sort(temp);
return (Collections.unmodifiableList(temp));
}
public static <T extends Comparable<T>> T[] asSortedArray(final Class<?> clazz,
final T ... vals)
{
final T[] temp;
temp = (T[])Array.newInstance(clazz,
vals.length);
System.arraycopy(vals,
0,
temp,
0,
vals.length);
Arrays.sort(temp);
return (temp);
}
public static void main(final String[] argv)
{
final List<String> list;
final String[] array;
list = MyClass2.asSortedList("c", "a", "b");
System.out.println(list);
array = MyClass2.asSortedArray(String.class, "z", "y", "x");
System.out.println(Arrays.deepToString(array));
}
}
the type constraint you want on the variable can't be expressed directly. you can introduce a new type to bridge the problem.
static class MyList<T extends Comparable<? super T>> extends ArrayList<T>{}
final MyList<?> values;
however, there is no point to be extremely type safe in a private piece of code. Generic is there to help you clarify your types, not to obfuscate them.
public class MyClass<T extends Comparable<? super T>> {
// This doesn't work as T isn't defined
final List<T> values;
public MyClass (T... values) {
this.values = new ArrayList<T>(Arrays.asList(values));
}
public List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}

Categories