I am trying to write a Java class, one part of which requires a mapping of the values of an unknown enum to another class. My class contains a field private Map<? extends Enum, Unit> myMap and is initialized with a factory method createMyClass:
public static MyClass <T extends Enum> myClass createMyClass(Class<T> x) {
MyClass theClass = new MyClass() //constructor is private
//...
myMap = new HashMap<T, Unit>();
for(T t : x.getEnumConstants())
myMap.put(t, theClass.new Unit(t));
//...
}
The class Unit is (and needs to be, as far as I can tell) an inner class of MyClass. When I put this into NetBeans it complains with this message:
method put in interface java.util.Map<K,V> cannot be applied to given types
required: capture #4 of ? extends java.lang.Enum, MyClass.Unit
found: T, MyClass.Unit
I understand (or at least I think I do) how the Collections need to be very careful about wildcard usage to preserve type safety, but I can't think of how T extends Enum fails to match ? extends Enum.
Try changing your method declaration to:
public static <T extends Enum<T>> MyClass createMyClass(Class<T> x) {
}
What Map<? extends Enum, Unit> means is "The key is some specific class that extends Enum, but I don't known which one". And since you don't know which class it is, you cannot add anything to such a Map, you can only get elements from it and be certain they are Enums.
You should probably just declare that field as Map<Enum, Unit> - it will allow all Enum subclasses just fine.
This seems to be the most common misunderstanding about Java enums by a huge margin - people see that ? wildcard and think they have to use it to allow subclasses.
You can read this Generics Tutorial. It explains why this doesn't work in section 4 (Wildcards).
As far as I understand, ? extends Enum can be any enum, not just a T or subclass of T.
Related
I have
1) a basic interface,
2) a few classes that implement this interface,
3) and a generic class that I want to accept, as a parameter, any of the implementing classes
I have tried the following:
public class Foo extends Bar<? extends SomeInterface> {
public Foo(List<? extends SomeInterface> someInterfaceList) {
super(someInterfaceList);
}
...
}
I receive the error No Wildcard Expected. Elsewhere in my code I have statements such as List<? extends SomeInterface> and I receive no errors, so why am I running into problems here? How can I fix this problem and still get the desired results?
I have tried search 'No Wildcard Expected' and 'wildcard in class declaration' to no avail. Thanks in advance!
It sounds like you want to declare a generic type argument that you will reference elsewhere. Wildcards only make sense when the type is used only once, and when declaring a generic type parameter for a class this doesn't make any sense.
Try this instead:
public class Foo<T extends SomeInterface> extends Bar<T> {
public Foo(List<T> someInterfaceList) {
super(someInterfaceList);
}
...
}
As your code was written, there was no way for the user of your class to specify the generic type argument for Bar<>, since Foo wasn't itself a generic type.
Further, if this were possible, it would have been possible for the generic argument to Bar<> to be different than the generic argument to List<> -- as long as both types implemented SomeInterface there would not be a compile-time issue with these definitions, but there could have been a much more confusing error message later when you incorrectly assumed that both types must be the same.
So, declare the generic type once as a generic argument to the Foo class, and then use that type (T in my example) elsewhere to refer to that type instead of accepting some new generic type argument that may not refer to the same type.
I'm not exactly sure what you're looking for, so it might help if you could provide a little more detail. Perhaps you could be a little more specific about how you're planning to instantiate and use these objects?
Anyways, I think you might be looking for something like this:
import java.util.List;
public class Foo<T extends SomeInterface> {
public Foo(List<T> someInterfaceList) {
for (T item : someInterfaceList) {
// do something with each item
}
}
}
class Bar<T> {}
interface SomeInterface<T> {
T x(T y);
}
Or, alternatively, you could just use the following for the constructor:
public Foo(List someInterfaceList) {
but you wouldn't have an easy way of getting the type T of the items in the list.
I dont understand a strong defenition of type parameter in generic class or method. It's a reference type such that... what? Does it just non reifiable type? Is it true that all reference type for compiler is reifiable or not reifiable?
2.Consider the well known code:
List<? extends String> strs= new ArrayList<String>();
? extends String str;//error
strs.add("sd");//error
Why ? extends String str; does not valid? I thinked that if we declare a reference to a generic type, for example List<E> strs; for some type E then compiler define type parameter to a specific type E.
I dont understand what occur in the case List<? extends String> strs? Does List<? extends String> and , for example List<String> parsing similarly at compile time or List<? extends String> parsing different from List<E> for some reifiable type E?
Generics are used to specify a type.
Consider the following class def:
public interface List<E> extends Collection<E>
This tells us there we have a List that can hold elements of any type.
Notice how there are no restrictions on E.
If we define a class like so:
public class MyList<T extends String> implements List<T>
We now have a MyList that implements List, but only accepts Strings (or decedents).
Inside this class we can refer to T.
public class MyList<T extends String> implements List<T> {
private ArrayList<T> internalStorage;
Key point
In the class definition we have defined what T is; it's any class based on String.
Inside the class T can thus be referenced.
However the flavor of T does not get fixed until the class actually gets instantiated:
MyList<MyStringType> test = new MyList<MyStringType>(parameters); //java 6
MyList<MyStringType> test = new MyList<>(parameters); //java 7, same but shorter.
Now Java knows what T means inside the MyList class T is a MyStringType.
Armed with is knowledge Java compiles the class and replaces all references to T with references to the actual class MyStringType.
It completely forgets about T.
Now inside the class note that everywhere T is mentioned it will get replaced by MyStringType.
But what if I want to handle a string, but not necessarily the MyStringType.
Solution:
I define a member like so:
List<? extends String> strs; //Will fill the data in later
Now we have a List called strs that will only accept strings, but that will not be forced to use strings of type MyStringType.
This list is not bound to the definition of T that was fixed when MyList was instantiated.
When we assign a value to the strs, the flavor of the List if fixed. In your example it will be a List of String.
? extends String str;//error
The variable cannot be fixed, because its type cannot be nailed down when the class that holds it gets created.
Generics is designed for read and not for write. This is why you can use generics in parameters for a method, but cannot use it for declare a variable that you can use after for read/write from it :-)
As per my Previous Question, I am reading the article from Angelika Dissecting Enum. Except for the points that a type can only be instantiated for its subtypes and the subtypes do inherit some common methods, I am not able to understand the article.
What is the meaning of abstract Enum class declared in this way? How is it helpful?
The document in the last part has described three aspects, can someone explain them in easier terms to me?
I do see in the code sketch the Enum class is declaring the compareTo method. When Enum is implicitly implementing Comparable interface. Why do it needs to define its own compareTo method?
Seems like it is a concept of recursive generics. What does recursive generics exactly mean? After doing a bit of R&D and understanding my last question answer, I understand that it forces the class to be parameterized on itself.
Still, a detailed explanation would be useful.
I think the main benefit of declaring generic types as Type<E extends Type<E>> is that such generic classes will make subclasses to inherit methods which return or accept arguments with subtype's type. Such methods in java.lang.Enum are:
public final int compareTo( E o) { ... }
public final Class< E > getDeclaringClass() { ... }
So, if we declare the enum Color, that implicitly means:
public class Color extends Enum<Color>
so in this instantiation of Enum the type paramater E is assigned the type argument Color, so the above methods will look like these:
public final int compareTo(Color o) { ... }
public final Class<Color> getDeclaringClass() { ... }
When saying something like Enum<Color extends Enum<Color>>, that sounds like you are declaring a generic type parameter Color that makes sure that it extends Enum with a type parameter matching Color.
But that isn't where generic type parameters for a class are declared. You must declare them next to the class name; you can only use them later in the extends clause. E.g.
// Use "extends" here ... not here.
public class MyClass<E extends MyClass<E>> extends MySuperClass<E>
In this example, you are declaring the class Color to be the value of the generic type parameter that is already defined on Enum.
I have a class defined like so:
public class AddRecordsToolbar<D extends IDataSource<T>, T extends Serializable>
extends AbstractToolbar<D, T>
which my IDE IntelliJ IDEA declares as legal. It looks and feels wrong to me.
I want to declare it like this:
public class AddRecordsToolbar<D extends IDataSource<T extends Serializable>, T>
extends AbstractToolbar<D, T>
however that syntax is illegal thanks to something to do with Javas type erasure.
D extends IDataSource<T> is required by the superclass.
My Class is using Serializable to do a deep copy. Hence the T extends Serializable.
So now on to the Question: If I specify T extends Serializable as the second type parameter for my class will it still enforce T extends Serializable for D as well?
Answering to your question, yes its do.
The order of generic parameter it only in your mind.
If we would rephrase that implementation to:
public class AddRecordsToolbar<T extends Serializable, D extends IDataSource<T>> extends AbstractToolbar<D, T>
you will be not so surprised, and looks that the way it should be.
I will try to find the explanation for this in Java Language Specification (when it will work) but for now that the way it is.
I believe that this is addressed in section 8.1.2 of the java language spec:
The scope of a class' type parameter is the entire declaration of the
class including the type parameter section itself. Therefore, type
parameters can appear as parts of their own bounds, or as bounds of
other type parameters declared in the same section.
Yes. The "T" in the second parameter is the same T as in the first. Nothing world work, otherwise.
This means that
class IFoo extends IDataSource<String>{};
AddRecordsToolbar<IFoo, Integer> x;
Is illegal. Integer and String are both serialisable, but the declaration of AddRecordsToolbar says that the data source has to be a source of the data type in your second parameter. And that second parameter says that it has to be serializable.
I'd like to define a generic type, whose actual type parameter can only be
One of the numeric primitive wrapper classes (Long, Integer, Float, Double)
String
I can meet the first requirement with a definition like this
public final class MyClass<T extends Number> {
// Implementation omitted
}
But I can't figure out how to meet both of them. I suspect this is not actually possible, because AFAIK there's no way to specify "or" semantics when defining a formal type parameter, though you can specify "and" semantics using a definition such as
public final class MyClass<T extends Runnable & Serializable > {
// Implementation omitted
}
Cheers,
Don
Java generics does not support union types (this parameter can be A OR B).
On a related note that may be of interest to some, it does support multiple bounds, if you want to enforce multiple restrictions. Here's an example from the JDK mentioned in the Java generics tutorial:
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
You could use factory methods for all supported types and make the constructor private/protected. You have to fix the generic type in the constructor anyway so that it makes sense, so you could probably code it like this:
public final class MyClass<T> {
public static MyClass<Integer> newInstance(int i) {
return new MyClass<Integer>(i);
}
public static MyClass<String> newInstance(String s) {
return new MyClass<String>(s);
}
//More factory methods...
protected MyClass(T obj) {
//...
}
}
Or if you do not want the constructor parameter, something like this:
public final class MyClass {
public static MyClass newIntegerInstance() {
return new MyClass();
}
//...
}
As erickson stated, the common implementation can rely only on Object anyway, so the only restriction is, that you can create other implementations for other types besides the primitive and String.
While generics won't work here, a base type with derived types for Number and String will. Since a generic type would have erased to Object anyway, any functionality you would have put there can go in an abstract base class. You're likely to need only a type-specific accessor on the subclass to get the value.
Also, be careful with the Number class. It's not limited to boxing the primitive types, as anyone can extend it—e.g., BigInteger.
Interesting question, it boggled me a bit. However apparently this is impossible. I tried several different hacks, none really work.
Maybe you could do what follows:
Make MyClass<T> a package-default class, invisible to other components, or at least with a package-default ctors only, so that it cannot extended or instantiated outside the package.
Create two public classes in the package of MyClass<T>:
MyNumericClass<T extends Number> extends MyClass<T>
MyStringClass extends MyClass<String>
This way all subclasses of MyClass will be limited to those parametrized with a Number subclass or String.