If I have an ArrayList<Shape>, I'm under the impression that within that list I can store objects of class Shape or objects that are subclasses of Shape.
I've also seen a lot of notation around with things like ArrayList<T> or ArrayList<?>. Are these the same things? A T superclass and all its subclasses can be stored, and the ? is just a placeholder?
Basically, how does something like ArrayList<CLASSNAME> differ from ArrayList<T> where you just have the one letter.
First, the difference is not whether it is one letter as you might think. It is whether T is the name of a generic argument or not. If you declare a method like:
public <T> void method()
as opposed to:
public void method()
or a class like:
public class Whatever<T>
as opposed to:
public class Whatever
then T is a generic argument. You then get to use T anywhere you want as if it were a real class name, and a real class name will be substituted for T at compile time. But this is not the best picture of it, so let's say that the type of T is whatever type was passed to the arguments of type T. So, at any given moment, T has a value that is a real class name.
So, the difference between ArrayList<T> and ArrayList<Shape> is that ArrayList<T> holds objects of type T, whereas ArrayList<Shape> holds objects of type Shape. The trick is that T is whatever type was passed to arguments of type T, so it can vary.
Disclaimer: The type T does not actually change over time. In most cases, the compiler replaces it with Object. However, the compiler is good at hiding that, and it is a good idea to think of T changing.
First of all, ArrayList can't reference any subclass of Shape like this:
ArrayList<Shape> blah = new ArrayList<Square>
But you can do it this way:
ArrayList<? extends Shape> blah = new ArrayList<Square>
Polymorphism doesn't work with type parameter. It only works with objects such as:
Collection<String> blah = new ArrayList<String>
But you have to understand the purpose of using T. T is used to declare a type parameter for class and methods.
Such that when you declare a class with parameter T:
public class ArrayList <T>
T acts as a placeholder when the class is instantiated as object.
So in this case:
ArrayList<String>
you are replacing T with String in the ArrayList class.
Btw, you aren't suppose to use T as a type for argument. Because T isn't really defined, it is only a placeholder.
You express ArrayList<T> when you're declaring the type / class and ArrayList<String> when you're creating an instance of ArrayList and associate it with the type parameter String.
For example:
// class declaration
public class ArrayList<T> extends ... implements ... {
...
// Creating an arraylist of string
ArrayList<String> stringArray = new ArrayList<String>
You can think of generics as a type variable. Hence instead of creating a class which only works with String:
public class MyArray {
String get(int index) { ... }
}
You can create an array class that works with any arbitrary type
public class MyArray<T> {
T get(int index) { .. }
}
Related
I have a method, that maps elements of a collection to other object and returns a collection containing the mapped elements. I want the returned Collection to be of the same Collection-type as the input Collection, but with a different element type.
My method would look something like this:
<E extends OriginalElement, T extends TargetElement,
C extends Collection<E>, R extends C<T>> R map(C elementsToMap) {
// snip
}
Obviously the part R extends C<T> doesn't work.
How can I specify that return type to be the same subclass of Collection as Type C, but with element type T instead of E?
You can't, I don't think, because e.g. ArrayList<String> and ArrayList<Integer> are essentially unrelated types.
Plus, when you say "same generic Collection-type", do you mean:
"if I give you some subclass of ArrayList, you'll give me back an instance of java.util.ArrayList"; or
"if I give you a specific subclass of ArrayList, you'll give me back an instance of the same specific subclass of ArrayList"?
Not only is that hard, because in general you don't know how to instantiate arbitrary subclasses, you might not be able to create such an instance, for example if the input is an IntegerArrayList (extends ArrayList<Integer>), and you want to map the elements to Strings. So, whilst you could return a java.util.ArrayList<String> in that case, you can't have a generic solution because you need some knowledge of "which type to instantiate in this specific case".
I am going to make an unquantified assertion that a small handful of collection types can handle most cases. So, provide overloads for these specific types:
</* type variables */> ArrayList<T> map(ArrayList<E> input) { ... }
</* type variables */> HashSet<T> map(HashSet<E> input) { ... }
</* type variables */> ImmutableList<T> map(ImmutableList<E> input) { ... }
// etc.
and then provide a general method for the other cases, and leave it up to callers to specify the collection type they want:
</* type variables */> Stream<T> map(Collection<E> input) { ... }
and then call the general method from the specific methods:
</* type variables */> ArrayList<T> map(ArrayList<E> input) {
return map((Collection<E>) input).collect(toCollection(ArrayList::new));
}
// etc.
I am new to generics and read in a article "A parameterized type, such as ArrayList<T>, is not instantiable — we cannot create instances of them".
Full quote, from Java in a Nutshell:
A parameterized type, such as ArrayList<T>, is not instantiable - we
cannot create instances of them. This is because <T> is just a type
parameter - merely a place-holder for a genuine type. It is only when
we provide a concrete value for the type parameter, (e.g.,
ArrayList<String>), that the type becomes fully formed and we can
create objects of that type.
This poses a problem if the type that we want to work with is unknown
at compile time. Fortunately, the Java type system is able to
accommodate this concept. It does so by having an explicit concept of
the unknown type which is represented as <?>.
I understand that it should not be instantiable since the concrete (actual) type is not known. If so, why does the below code compiles without an error?
public class SampleTest {
public static <T> List<T> getList(T... elements) {
List<T> lst = new ArrayList<>(); // shouldn't this line return an error?
return lst;
}
}
I know there is a gap in my understanding of generics here. Can someone point out what am i missing here?
Because T is given as another generic type argument.
It's the whole purpose of generics to make the type parameterizeable. So the caller can specify the type. This can be done in multiple layers: the caller may also be generic and let its caller specify the type.
public static void main(String[] args)
{
foo(7);
}
public static <T> void foo(T value)
{
bar(value);
}
public static <U> void bar(U value)
{
baz(value);
}
public static <V> void baz(V value)
{
System.out.println(value.getClass().getSimpleName());
}
It prints out
Integer
A parameterized type, such as ArrayList<T>, is not instantiable
Means: You cannot create ArrayList of an unknown T. It must be specified at compile time. But it can be done indirectly, by another generic. In your case, it's another T, which will be specified again by the caller of your generic getList.
The wildcard <?> is something different. It is used to specify compatibility. <?> is the syntax to avoid specification of the type. You can use extends to require a basetype or interface. However, you cannot create instances with wildcards.
List<?> list = new ArrayList<String>();
list = new ArrayList<Integer>();
This wouldn't be possible otherwise. It makes most sense when using it in parameter specifications, for instance:
public static int foo(List<? extends Comparable> list)
{
return list.get(1).compareTo(list.get(2));
}
It's very confusing of this book. It assumes that <?> somehow solves the problem that a List with unknown T cannot be instantiated. IMHO, this is rubbish. T must be specified to create an instance.
The code that you mention can compile because the Object "lst" is not actually initialized until the method is called. Since the method knows that it will be getting a var-args argument of type T, it can compile in this scenario. Take the example Wrapper class below for example:
public class Wrapper<T> {
public static <T> List<T> getList(T... elements){
List<T> lst = new ArrayList<>();
for(T element: elements) {
lst.add(element);
}
return lst;
}
}
This code can compile because the method hasn't been called. When the method is called, Type T will be the type that we pass as the var-args argument and the code will have no issue compiling. Lets test this in our main method:
public static void main( String[] args ){
System.out.println(Wrapper.getList("Hi", "Hello", "Yo"));
}
And the output is:
[Hi, Hello, Yo]
However, lets generate a compile-time error to see what the article is talking about within our main method:
Wrapper<T> myWrap = new Wrapper<>();
We are actually trying initialize a generic Object of the Wrapper class in the code above, but is unknown. Since the value for the placeholder will be unknown even when we call the method, it results in a compile-time error, whereas creating a List of type T within the getList method does not cause a compile-time error because it will be initialized with a type when the method is called.
once you call the method -> you are using a concrete value.
the method defines T and later you use it in the return type and the parameter list.
public static <T> List<T> getList(T... elements)
once you will send the first parameter from specific type -> the contract will force you for the next parameters.
List<? extends Object> list = getList("", 1); -> in this case java doesnt find common between string and integer so it uses the most basic connection "Object"
List<String> list2 = getList("test", "test2"); -> here you can see that because all of the parameters are Strings - java find that in common and use it as the T.
The specific passage from the book doesn't make any sense and is wrong. new ArrayList<T>() is perfectly fine provided that we are in the scope of a type parameter named T (either a type parameter of a generic class that we are in, or a type parameter of the generic method we are in).
new ArrayList<T>() can no less be instantiated than new ArrayList<String>() -- both compile to the same bytecode and both just instantiate an ArrayList object at runtime. The object doesn't know anything about its type parameter at runtime, and therefore no knowledge of T at runtime is needed to instantiate it. The type parameter in an instantiation expression of a parameterized type (new ArrayList<T>) is just used by the compiler to type-check the parameters passed to the constructor (there are none in this case) and to figure out the type returned by the expression; it is not used in any other way.
And by the way the method does not need to receive any parameters of type T, or of any type containing T, in order for this to work. A method that receives no arguments can still instantiate and return an ArrayList<T> perfectly fine:
public static <T> List<T> emptyList() {
List<T> lst = new ArrayList<T>();
return lst;
}
Also, the section in the book where this statement appears in doesn't really have anything to do with instantiation -- the section is about wildcards, and wildcards don't really have anything to do with object instantiation at all. So I am not really sure why they are mentioning it (incorrectly) there.
public class GenericClass<T> {
class MyClass {
}
public GenericClass(final T[] param) {
MyClass myObject = new MyClass(); // OK
MyClass[] myArray = { new MyClass(), new MyClass() }; // Cannot create a generic array of GenericClass<T>.MyClass
}
}
This is not creating a generic array. The compiler should have no problems understanding/determining MyClass, isn't it?
Inner classes "know" which instance of the enclosing class created them, and can access fields/members of this instance. It is as if they have a second this variable whose type is the concrete type of the enclosing class (such as GenericClass<String>).
To overcome this predicament you can make MyClass static. This will make it completely decoupled of any instance of the enclosing class (that is: it will not have that second this) so they can be instantiated freely:
public class GenericClass<T> {
static class MyClass {
}
public GenericClass(final T[] param) {
MyClass myObject = new MyClass(); // OK
MyClass[] myArray = { new MyClass(), new MyClass() };
}
}
Here's some additional information. From the link ...
Java arrays carry runtime type information that identifies the type of
the elements contained
to the compiler your code looks like this:
MyClass[] myArray = {new GenericClass<T>.MyClass(), ..} //T is unknown
The JLS section that covers this is 10.6. Specifically, it's because:
It is a compile-time error if the ClassOrInterfaceType does not denote a reifiable type (§4.7). Otherwise, the ClassOrInterfaceType may name any named reference type, even an abstract class type (§8.1.1.1) or an interface type (§9).
The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard.
Because MyClass is non-static it is dependent on the outer class; it's actually GenericClass<T>.MyClass and therefore a parameterized type. Declaring it static removes that dependency and solves the problem.
Where it gets weird is if you do this;
class MyClass<T> {
}
public GenericClass(final T[] param) {
MyClass[] myArray = { new MyClass(), new MyClass() };
}
It's legal. Screwy, kind of clumsy, but legal. Because you redeclare the type, it hides the outer one. Then ... arrays and generics don't mix ... unless you use raw types. For backward compatibility you can have a rawtype array which ends up holding MyClass<Object>. It's a really awful thing, but it does compile. You can get away with creative casting here but in the end ... just ... don't.
{ new MyClass(), new MyClass() }; //new MyClass() => new GenericClass<T>.MyClass()
Above code will be treated as array of object as T is unknown ,due to the way generics are implemented (by erasure), the type of the array is not well-defined. On one hand, it should be an array of MyClass , on the other hand, it should be an array of Object
Create array of object type and cast it to your type
Object[] arr=new Object[]{this.new MyClass(), this.new MyClass()};
MyClass[] myArray = Arrays.copyOf(arr,arr.length, Item.MyClass[].class);
If you make it static it will work because-
A static nested class or nested interface (which is always static, by the way) has no relation to its outer class (or interface) apart from namespace nesting and access to private variables.
As an example in the standard API, look for the interface Map.Entry, nested inside the interface Map, yet has no access to its type parameters and needs to declare them again.
The problem here is that the compiler cannot determine at compile time the information of the array myArray. It is considered generic because (as eclipse shows you) it is converted in {new GenericClass<T>.MyClass(), ...}. This is because you're putting the class MyClass inside a generic class.
This code doesn't work either:
package my.stuff;
public class GenericClass<T> {
class MyClass {
static MyClass[] myArray = { new MyClass(), new MyClass() };;
}
public GenericClass(final T[] param) {
MyClass myObject = new MyClass();
}
}
but this code works:
package my.stuff;
public class GenericClass<T> {
public GenericClass(final T[] param) {
MyClass myObject = new MyClass();
MyClass[] myArray = { new MyClass(), new MyClass() };
}
}
class MyClass {
}
Because you're not using generics in your MyClass, the best thing to do is probably the second one.
If you declare it static, the compiler knows that MyClass is not generic and it knows what to do.
Besides, the only way to create a generic array in java is create a raw type and then cast it to generics (see here: "Cannot create generic array of .." - how to create an Array of Map<String, Object>?). So, if you absolutely need myClass inside the generic one, you should turn it in MyClass<T>, and then you use the trick: create a raw type and cast it to MyClass<T>:
package my.stuff;
public class GenericClass<T> {
class MyClass<T> {
}
#SuppressWarnings("unchecked")
public GenericClass(final T[] param) {
MyClass<T> myObject = new MyClass<T>();
MyClass<T>[] myArray = new MyClass[]{ new MyClass<T>(), new MyClass<T>() };
}
}
even it you don't use T inside the class MyClass.
#ItayMaman has the right reason. Basically, MyClass is not a reifiable type.
MyClass is a non-static inner class. Since it is non-static, it is within the scope of the type parameters of its enclosing class. And every time you write MyClass by itself in an instance method of GenericClass, it is actually short for GenericClass<T>.MyClass. So even though it may not look it, MyClass (by itself) is actually a parameterized type (parameterized by T), similar to List<String>. And so when you do new MyClass[2], you are trying to create an array of a parameterized type, just like new List<String>[2]. And I think you already know that this is not allowed.
What should you do? It all depends on what your intention is. One thing that people suggest is to make MyClass static. Of course, that will take it out of the scope of T. But that may or may not be what you want, because it completely changes its relation to GenericClass. A non-static inner class has access to an instance of the enclosing class, which is perhaps why you made it that way in the first place. If you never intended for it to be non-static (and did it by mistake), then this is obviously the way to go.
If a non-static inner class is what you want, and you simply wants to create an array of this type, let's consider how you would usually deal with arrays of parameterized types, e.g. List<String>[].
One solution is to instead create an array of the raw type, e.g. List[] foo = new List[2];. The equivalent way to do this for our case would be GenericClass.MyClass[] foo = new GenericClass.MyClass[2];. Notice what we did here. In order to write the raw type, we had to explicitly qualify MyClass with the unparameterized outer class name. If we didn't explicitly qualify it, then it would be implicitly qualified with GenericClass<T>, as explained above, which is not what we want. Translating this to the code in your example, you would write GenericClass.MyClass[] myArray = { new MyClass(), new MyClass() };
Similarly, if we want to avoid raw types, we could create an array of the wildcarded type, e.g. List<?>[] foo = new List<?>[2];. The equivalent way to do this for our case would be GenericClass<?>.MyClass[] foo = new GenericClass<?>.MyClass[2];. So translating this to the code in your example, you would write GenericClass<?>.MyClass[] myArray = { new MyClass(), new MyClass() };
Finally, we might instead want to create an array of the wildcarded type, but then cast back into an array of the parameterized type, for convenience of use later on. e.g. List<String>[] foo = (List<String>[])new List<?>[2];. The equivalent way to do this for our case would be MyClass[] myArray = (MyClass[])new GenericClass<?>.MyClass[] { new MyClass(), new MyClass() };. Note the the cast is an unchecked cast. The advantage of this is now when you get things out of myArray, it will be type MyClass, instead of raw type GenericClass.MyClass or wildcarded type GenericClass<?>.MyClass from the methods above.
http://docs.oracle.com/javase/tutorial/java/generics/genmethods.html
Quoting from there-
A more realistic use of generic methods might be something like the following, which defines a static method that stuffs references to a single item into multiple boxes:
public static <U> void fillBoxes(U u, List<Box<U>> boxes) {
for (Box<U> box: boxes {
box.add(u); }
}
Here, what does this List<Box<U>> do? How does this work?
Further,
To use this method, your code would look something like the following:
Crayon red = ...;
List<Box<Crayon>> crayonBoxes = ...;
The complete syntax for invoking this method is:
Box.<Crayon>fillBoxes(red, crayonBoxes);
I couldn't understand all these.
You can think of U as a kind of placeholder for a Type or Interface.
If you have a class Foo and replace all U's in the method declaration with Foo, it will take an instance of Foo and a List>.
As the name "Generic" says, it is a generic way of "replacing" the generic type argument U with a real type. This way you can force that everyone who uses an object of Type U also has to use a List> to call the method.
Taking the Foo example, I can only call
public static <U> void fillBoxes(U u, List<Box<U>> boxes) {
with
Foo foo = new Foo();
List<Box<Foo>> fooList = new ArrayList<Box<Foo>>();
fillBoxes(foo, fooList);
List is a generic collection.
Box is another generic.
You can declare Box<Crayon> to construct a Box of Crayons
List<Box> is a collection of Boxes but Box is generic, need a Type for the box List<Box<Crayon>>.
Just follow the generic type U. FillBoxes method is static public static <U> void fillBoxes(U u, List<Box<U>> boxes). Static methods doesn't need a instance, if there is not a instance you can't declare the type of the box Box<Crayon>, you have to pass the type in the call of the method Box.<Crayon>fillBoxes, knowing that type, the compiler knows that the first parameter in FillBoxes (U u) is a Crayon type and the second is a List of Boxes of Crayons (List<Box<U>> boxes).
The method will fill a list of Crayon Boxes with red crayons :)
Ufff this was hard for a non Java programmer and non native english speaker...
List<Box<U>> simply means it's a list of boxes where you can store U-type objects in each of them.
If for example you have classes "crayon", "pencil", "pen" etc. implementing interface "U", you can store their instances in such boxes.
"When you declare a type parameter for the class, you can simply use that type any place that you'd use a real class or interface type. The type declared in the method argument is essentially replaced with the type you use when you instantiate the class.
If the class itself doesn't use a type parameter, you can still specify one for a method, by declaring it in a really unusual (but available) space-before the return type, This method says that T can be "any type of Animal"."
Can you explain?
What it means is that in a generic class, you can write methods like so:
public T doSomething () {
}
Note that the return type is 'T'.
At compile-time, the return type of that method will be whatever you have passed to the generic class when you instantiated it.
class Zoo<T> {
static void putAnimal(T animal) {
// do stuff
}
}
Zoo<Ape> apeZoo = new Zoo<Ape>(); // you can now put apes (and chimps) here
Zoo<Reptile> monkeyZoo = new Zoo<Reptile>(); // this zoo takes reptiles
apeZoo.putAnimal(new Chimp());
monkeyZoo.putAnimal(new Tortoise());
For the first paragraph, this is just how generics work for classes. For instance, for list, you can create a list of a generic type, such as integer, e.g.:
ArrayList<Integer> list = new ArrayList<Integer>();
(in real code you'd use List<Integer> of course)
Now ArrayList will be defined as:
public class Arraylist<T> { // implements....
// ...
public T get(int index) {
// ...
}
}
Which is what makes it possible to use the get method on list and get an Integer (because we made a class of type ArrayList<Integer> so T = Integer). Otherwise the compiler would have no idea what types of objects the list was storing and you'd have to get the method to return an Object, which is how it used to be.
What the second paragraph means is that you can add type parameters to methods just as you can to classes. e.g.:
public <T> void noOp(T element) {
// code here
}
This would allow you, for instance, to create a static utility method that returns something of type T. To return the first element of a List of T's:
public static <T> T getFirst (List<T> list) {
return list.get(0);
}
And you could use this method in a strongly typed fashion. Suggestions for better examples welcome. :-)
edit: I just realised I once wrote something that uses this functionality. I was using the JPA API and getting really annoyed at all the times you have to return something (a list, or a single item) from a query, and running into unchecked type warnings because there's no way to infer the type here. If you're like me and trying to avoid warnings in your code, you'd have to suppress the warnings every single time. So I wrote this method to suppress the warnings for me:
#SuppressWarnings("unchecked")
public static <T> List<T> returnResultList(Query query) {
return (List<T>)query.getResultList();
}
Which through the magic of type inference works on:
List<Integer> list = returnResultList(query);