The following code compiles (and runs tests as expected) in Eclipse:
import java.util.EnumSet;
public class EnumTest {
static enum Cloneables implements Cloneable {
One, Two, Three;
}
public <T extends Cloneable> T getOne(Class enumType) {
EnumSet<? extends T> set = EnumSet.allOf(enumType);
return set.iterator().next();
}
}
However, compiling with either javac (JDK 7) directly or via Maven fails with the following error:
type argument ? extends T is not within bounds of type-variable E
To be honest, the complexity of enums + interfaces + type-parameters (generics) all at play at once threw me off as I was writing the code, but I thought I had finally gotten it right.
The goal is to write calling code like this:
Cloneable something = enumTest.getOne(Cloneables.class);
For example, in Eclipse the following test compiles and passes:
#Test
public void testGetFirst() {
assertSame(Cloneables.One, getOne(Cloneables.class));
}
Any clues about which is "correct," Eclipse or javac, are appreciated.
Also appreciated is any advice about alternate ways to implement the idea: take a class as a method param that can be used in EnumSet.allOf() and that also determines the type of Enum objects in the EnumSet
By the way, don't bother critiquing the purpose of this method; I've reduced it down from more useful/meaningful code. I'm not interested in discussing the merits of "finding the first element from an enum type" - that's not the point of this question.
You need to make sure that T is an enum type, or it won't meet the constraints for EnumSet:
public <T extends Enum<T> & Cloneable> T getOne(Class enumType)
Also, you don't need the wildcard in your EnumSet, and you shouldn't use the raw Class type:
public <T extends Enum<T> & Cloneable> T getOne(Class<T> enumType) {
EnumSet<T> set = EnumSet.allOf(enumType);
return set.iterator().next();
}
Related
I am getting this compiler warning.
This is my class with interface and method used (other staff omitted):
public class Controller extends BaseController {
//interface
public interface MyInterface<T extends Iterable<? super T>> {
List<T> getList();
}
//method call
updateJoinTable(request.getProductGrades().size(), instance::getProductGrades);
//method
private static void updateJoinTable(Integer LastValidElement, MyInterface myInterface) {
myInterface.getList().subList(LastValidElement, myInterface.getList().size())
.forEach(i ->myInterface.getList().remove(i));
}
}
The last part with forEach is causing warning.
Now, at first the code was without:
<T extends Iterable<? super T>>
but I've seen a lot of similar cases here on SO, mostly with comparable and I understood by reading the solutions, that problem is that my generic types are bonded to a type that itself has a type, but I haven't provided one, so it's raw - then I added that line to interface and expected warning to go away. But it's still there.
Do you have any ideas what I should do to get rid of it?
The immediate issue is that MyInterface myInterface is a raw type. Make it non-raw, e.g.:
private static void updateJoinTable(Integer LastValidElement, MyInterface<?> myInterface) {
Additionally, you might want to consider not using forEach. It looks like you're just trying to chop off the tail of the list:
List<?> list = myInterface.getList();
list.subList(LastValidelement, list.size()).clear();
I am getting this name clash error and i don't know how should i solve the problem.
I have two classes and i am using overloaded method "createSensors". To simplify here is the code that generates the problem:
public abstract class ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassA> list) {
List<Sensor> sensors = new ArrayList<Sensor>();
for (ClassA s : list) {
sensors.add(s.getSensor());
}
return sensors;
}
}
public abstract class ClassB extends ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassB> list) {
List<Sensor> sensors = new ArrayList<Sensor>();
for (ClassB s : list) {
sensors.add(s.getSensor());
}
return sensors;
}
}
General answer :
Apart from the problem of the same implementation here, the core of the problem is that, somewhat barbaric, "method A and Method B have the same erasure".
What makes it a complicated question is that we generally don't (at least I did not this very morning) know a lot about "Type Erasure".
To make it short :
Parametric types perform type check at compile time (to ensure type correctness) but forget their type parameters at runtime (to avoid the generation of underlying methods).
This sounds at the same time simple and puzzling.
Best way to understand it is to refer to the following literature :
What is a reifiable type ?
How and under what conditions is erasure performed ?
Have you any idea/examples about what it could imply in my coding life ?
Well that's odd and I don't really like it but I'm curious why they did that ...
Hope that'll help you as much as it helped me.
Specific answer :
In your case
public abstract class ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassA> list) {
//do stuff
}
}
public abstract class ClassB extends ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassB> list) {
//do other stuff
}
}
will be "transformed" by javac to
public abstract class ClassA {
public static List createSensors(Collection list) {
//do stuff
}
}
public abstract class ClassB extends ClassA {
public static List createSensors(Collection list) {
//do other stuff
}
}
where one clearly can't override the other (not the same type parameter) but end up being exactly the same at runtime (no way for your program to choose which one to use).
Enough of this problem, how to solve it ?
You may proceed with one of the following approach :
Use different names : createASensors and createBSensors
this approach is the most obvious but would seem a little less elegant.
Add a parameter : createSensors(Collection<? extends ClassA> list, ClassA typeDefiner)
this approach can seem barbaric but is a little less elegant but is the one used in java.util.List for the method <T> T[] toArray(T[] a).
The general solution is to use different names. These methods could be in classes without an inheritance relationship as these are not instance methods.
As pointed out, the method implementation in the question are the same (typo excepted).
(This issue with overloading is often confused with erasure of runtime types. Overloading is a link-time rather than a dynamic issue, so could be easily fixed in the language. It's just not a particularly useful change, and not a good idea to encourage overloading.)
Check the project setting and compiler version of the project. Right click on project --> Properties --> Java Compiler. Make sure compliance setting are up to date. I had this problem when compliance settings were set to 1.4 instead 1.6
I am getting this name clash error and i don't know how should i solve the problem.
I have two classes and i am using overloaded method "createSensors". To simplify here is the code that generates the problem:
public abstract class ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassA> list) {
List<Sensor> sensors = new ArrayList<Sensor>();
for (ClassA s : list) {
sensors.add(s.getSensor());
}
return sensors;
}
}
public abstract class ClassB extends ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassB> list) {
List<Sensor> sensors = new ArrayList<Sensor>();
for (ClassB s : list) {
sensors.add(s.getSensor());
}
return sensors;
}
}
General answer :
Apart from the problem of the same implementation here, the core of the problem is that, somewhat barbaric, "method A and Method B have the same erasure".
What makes it a complicated question is that we generally don't (at least I did not this very morning) know a lot about "Type Erasure".
To make it short :
Parametric types perform type check at compile time (to ensure type correctness) but forget their type parameters at runtime (to avoid the generation of underlying methods).
This sounds at the same time simple and puzzling.
Best way to understand it is to refer to the following literature :
What is a reifiable type ?
How and under what conditions is erasure performed ?
Have you any idea/examples about what it could imply in my coding life ?
Well that's odd and I don't really like it but I'm curious why they did that ...
Hope that'll help you as much as it helped me.
Specific answer :
In your case
public abstract class ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassA> list) {
//do stuff
}
}
public abstract class ClassB extends ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassB> list) {
//do other stuff
}
}
will be "transformed" by javac to
public abstract class ClassA {
public static List createSensors(Collection list) {
//do stuff
}
}
public abstract class ClassB extends ClassA {
public static List createSensors(Collection list) {
//do other stuff
}
}
where one clearly can't override the other (not the same type parameter) but end up being exactly the same at runtime (no way for your program to choose which one to use).
Enough of this problem, how to solve it ?
You may proceed with one of the following approach :
Use different names : createASensors and createBSensors
this approach is the most obvious but would seem a little less elegant.
Add a parameter : createSensors(Collection<? extends ClassA> list, ClassA typeDefiner)
this approach can seem barbaric but is a little less elegant but is the one used in java.util.List for the method <T> T[] toArray(T[] a).
The general solution is to use different names. These methods could be in classes without an inheritance relationship as these are not instance methods.
As pointed out, the method implementation in the question are the same (typo excepted).
(This issue with overloading is often confused with erasure of runtime types. Overloading is a link-time rather than a dynamic issue, so could be easily fixed in the language. It's just not a particularly useful change, and not a good idea to encourage overloading.)
Check the project setting and compiler version of the project. Right click on project --> Properties --> Java Compiler. Make sure compliance setting are up to date. I had this problem when compliance settings were set to 1.4 instead 1.6
As a follow up to Java generics compile in Eclipse, but not in javac, I post another snippet which compiles and runs fine in Eclipse, but raises a compilation error in javac. (This prevents the project the snippet is extracted from, from being build with Maven.)
The self-contained snippet:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<Foo<?>> setOfFoos = new HashSet<Foo<?>>();
List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
}
public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
public static class Foo<T> implements Comparable<Foo<T>> {
#Override
public int compareTo(Foo<T> o) {
return 0;
}
}
}
Compilation in javac returns:
Main.java:11: <T>asSortedList(java.util.Collection<T>) in Main cannot be applied to (java.util.Set<Main.Foo<?>>)
List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
^
On substitution of Foo<?> with Foo<String> the above snippet will compile in javac, which means the problem is related to the used wildcard. As the Eclipse compiler is supposed to be more tolerant, is it possible the snippet is no valid Java?
(I use javac 1.6.0_37 and Eclipse Indigo with compiler compliance level 1.6)
(EDIT1: Included another example which got removed in EDIT2.)
EDIT2: Hinted by irreputable, that comparing Foo<A> and Foo<B> may be conceptually wrong, and inspired by the answer of seh, a working asSortedFooList can be written as follows:
public static <T extends Foo<?>> List<T> asSortedFooList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
(Simple substitution of Comparable<T> with Foo<?> in the method definition above.)
So it seems to be safe for javac and imho conceptually right to compare any Foo<A> and Foo<B>. But it is still not possible to write a generic method asSortedList which returns a sorted list representation for a generic collection, if its type argument is parametrized with a wildcard. I tried to "trick" javac by substituting Foo<?> by S extends Comparable<S> in asSortedFooList, but this didn't work.
EDIT3: Later Rafaelle pointed out, that there is a flaw in the design, since implementing Comparable<Foo<T>> is not necessary, and implementing Comparable<Foo<?>> provides the same functionality, solving the initial problem by refined design.
(The initial reason and benefit was, that a Foo<T> may not care in some purposes about its concrete type but still use an instance of a concrete type T, it is instantiated with, for other purposes. That instance does not have to be used for determining the order among other Foos, as it may be used in other parts of the API.
Concrete example: Assume each Foo is instantiated with a different type argument for T. Every instance of Foo<T> has an incrementing id of type int which is used in the implementation of the compareTo-method. We can now sort a list of these differently typed Foo and don't care about the concrete type T (expressing it with Foo<?>) and still have an instance of a concrete type T accessible for later processing.)
To me this is another javac bug. When you try to send a Collection<Foo<?>> to a method with the signature:
public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c)
the compiler notes that the formal parameter T has an upper bound, so checks if the constrained is honored by the caller. The type argument is a (wildcard) instantiation of the parameterized type Foo<T>, so the test will pass if Foo<?> is-a Comparable<Foo<?>>. Based upon the generic definition:
class Foo<T> implements Comparable<Foo<T>>
I'd say that it's true, so again Eclipse is right and javac has a bug. This Angelika Langer's entry is never linked enough. Also see the relevant JLS.
You asked if it is type-safe or not. My answer is that it is type safe, and it shows you have a flaw in your design. Consider your fictitious implementation of the Comparable<T> interface, where I added two more fields:
public static class Foo<T> implements Comparable<Foo<T>> {
private T pState;
private String state;
#Override
public int compareTo(Foo<T> other) {
return 0;
}
}
You always return 0, so the problem is not spotted. But when you try to make it useful, you have two options:
Comparing on the String field
Comparing on the T member
The String field is always a String, so you don't really benefit from the type variable T. On the other hand, T has no other type information available, so in compareTo() you can only deal with a plain object, and again the type parameter is useless. You can achieve the same exact functionality by implementing Comparable<Foo<?>>
In this case, javac is correct. Conceptually, your code can't work, since the set may contain Foo<A> and Foo<B>, which can't be compared to each other.
You probably want the set to be a Set<Foo<X>> for some type variable X; unfortunately we can't introduce type variable inside method body; only in method signature
<X> void test(){
Set<Foo<X>> setOfFoos = new HashSet<Foo<X>>();
List<Foo<X>> sortedListOfFoos = asSortedList(setOfFoos);
}
You may make it work by something like
<T extends Comparable<? super T>> List<T> asSortedList(Collection<T> c)
class Foo<T> implements Comparable<Foo<?>>
I don't know if this is a question, but here is a (not very nice) answer:
If you sacrifice some type safety you can write
#SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends Comparable> List<T> asSortedList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
And it works in both eclipse and javac. The only risk that I'm aware of is that if someone creates a class Foo extends Comparable<Bazz> you won't detect that in compile time.
But if someone creates Foo extends Comparable<Bazz>, just kill him/her.
I found a solution that compiles with javac, though I am not happy that I am unable to explain exactly why it works. It requires introducing an intermediary function:
public final class Main {
public static class Foo<T> implements Comparable<Foo<T>> {
#Override
public int compareTo(Foo<T> o) {
return 0;
}
}
public static <T extends Comparable<? super T>>
List<T> asSortedList(Collection<T> c) {
final List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
private static <T extends Foo<?>> List<T> asSortedFooList(Collection<T> c) {
return asSortedList(c);
}
public static void main(String[] args) {
final Set<Foo<?>> setOfFoos = new HashSet<Foo<?>>();
final List<Foo<?>> listOfFoos = asSortedFooList(setOfFoos);
}
}
I think that this works by virtue of taking the wildcard resolution step-by-step; asSortedFooList() captures one type known to be a Foo, irrespective of Foo's type parameter. With that type parameter bound in asSortedFooList(), we can then call on your original asSortedList() (well, with one modification—note the lower bound on the type parameter for Comparable) requiring binding Foo as a type descended from Comparable.
Again, that's a weak, haphazard explanation. My main point in answering here is just to provide one more way to get to your destination.
If you can replace your wildcard usage with an exact type (which may be a super-type) your code will work. Replace
List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
with
List<Foo<String>> sortedListOfFoos = Main.<Foo<String>>asSortedList(setOfFoos);
Last Thursday someone at work showed me a compile error that I wasn't able to fix in a clean way and it has been bothering me ever since.
The problem is generics related and I've reconstructed a simplified version of the code that generates the compile error. The error occurs in the very last line of code shown below.
I've been looking all over the interwebs but can't seem to find a decent explanation why the Java compiler doesn't accept the code. I guess that if it were to allow the code, it would be possible the create a class cast issue in Bar.operationOnBar(), but I don't see how.
Could someone please enlighten me why this doesn't compile?
public interface Interface {
}
public class Type implements Interface {
}
public class Bar<T> {
public Bar(Class<T> clazz) {
}
public void operationOnBar(Class<T> arg){
}
}
public class Foo {
public <T> Bar<T> bar(Class<T> clazz){
return new Bar<T>(clazz);
}
public static void main(String[] args) {
Class<? extends Interface> extendsInterfaceClazz = Type.class;
new Foo().bar(extendsInterfaceClazz).operationOnBar(Type.class);
}
}
Compile Error on the second line of Foo.main():
The method operationOnBar(Class<capture#1-of ? extends Interface>) in the type Bar<capture#1-of ? extends Interface> is not applicable for the arguments (Class<Type>)
Btw. I've solved it by downcasting Type.class to Class, this way the compiler is unable to see that the generic type of Class is "Type" instead of "? extends Interface".
A little advice: when you are not sure why compiler prohibits some generic-related conversion, replace generic classes in question with List<T>. Then it would be easy to find an example that breaks type safety.
This replacement is correct since currently Java doesn't provide a way to conduct any a priory knowledge about possible behaviours of generic classes (i.e. it lacks a way to specify covariance and contravariance of generic classes in their declarations, as in C# 4 and Scala). Therefore Class<T> and List<T> are equivalent for the compiler with respect to their possible behaviours, and compiler has to prohibit conversions that can cause problems with List<T> for other generic classes as well.
In your case:
public class Bar<T> {
private List<T> l;
public Bar(List<T> l) {
this.l = l;
}
public void operationOnBar(List<T> arg) {
l.addAll(arg);
}
}
List<Type1> l1 = new ArrayList<Type1>();
List<? extends Interface> l2 = l1;
List<Type2> l3 = Arrays.asList(new Type2());
new Foo().bar(l2).operationOnBar(l3);
Type1 t = l1.get(0); // Oops!
You also can change the signature of the method operationOnBar to:
public void operationOnBar(Class<? extends Interface> arg){
You would agree that this shouldn't compile:
1 Class<? extends Interface> clazz = AnotherType.class;
2 new Foo().bar(clazz).operationOnBar(Type.class);
The problem is javac is a little dumb; when compiling line#2, all it knows about variable clazz is its declared type; it forgets the concrete type it was assigned to. So what is assigned to clazz at line#1 doesn't matter, compiler must reject line#2.
We can imagine a smarter compiler that can track the concrete types, then your code can be compiled, as it is obviously safe and correct.
Since that's not the case, sometimes programmers know more about types than the compiler, it is necessary that programmers do casts to convince the compiler.
The general way to deal with these sorts of problems is to introduce a generic argument for the repeated type, which generally means introducing a new generic method (a class would do as well, but isn't necessary).
public static void main(String[] args) {
fn(Type.class);
}
private static <T extends Interface> void fn(Class<T> extendsInterfaceClazz) {
new Foo().bar(extendsInterfaceClazz).operationOnBar(extendsInterfaceClazz);
}
Not really related to the question, but I would suggest using reflection sparingly. It is very, very rarely a good solution.