I had read documentation about generics types and I know use it but I have a problem "reading" two methods.
I use "toArray()" and I use "asList()" but I don't understand how write the method solving types.
Example 1
public Iterator<E> iterator();
public Iterator<String> iterator();
Example 2
public E get(int location);
public String get(int location);
Example 3(Here is that I don't understand)
public static <T> List<T> asList(T... array) {
return new ArrayList<T>(array);
}
public static <String> List<String> asList(String... array) {
return new ArrayList<String>(array);
}
[modifiers] [return type] [¿What is this?] [type param]
public static <String> List<String> asList(String... array) {
v[type param for returned ArrayList]
return new ArrayList<String>(array);
}
Example 4(the same that Example 3)
public <T> T[] toArray(T[] array);
[modifier] [return type] [what is this?] [param type]
public <String> String[] toArray(String[] array);
¿Is this a return type?
<String> List<String>
I had read the forum but I don't find some that explain this. All responses are explanations of how to use it but I know how to use it.
Thanks in advanced!
===================== EDIT 1 =============================
I have a test:
import java.util.List;
public class ClassTest {
public static void main(String[] args) {
ClassTest.testClass(new String[]{"1","2","3"});
}
public static <String> List<String> testClass(String[] array){
System.out.println("** public static <String> List<String> testClass(String[] array){");
return null;
}
public static List<String> testClass(String[] array){
System.out.println("** public static List<String> testClass(String[] array){");
return null;
}
}
If I execute the test I have this trace:
** public static List<String> testClass(String[] array){
If I delete the second method I have this trace:
** public static <String> List<String> testClass(String[] array){
In both case, it works.
Maybe is not the same that my first question but I think so.
The compiler think that the methods are diferent from the other because if y delete
<String>
in the first method compiler says that the method is repeat.
I can't appreciate your response.
===================== EDIT 2 =============================
I have some new information... In Oracle doc exist this:
https://docs.oracle.com/javase/tutorial/java/generics/methods.html
https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html
Exactly, what I ask is called: Type Inference(I don't know, at the moment...) related with "Generic Methods".
At present, I'm reading this documentation...
I will be back when I read it.
Thanks for all!
PD: I don't forget completly this thread because maybe I come back to ask.
===================== EDIT 3 =============================
I've read the Oracle's documentation and I think I understand it a little bit more.
I think this is the best way to understand it...
In a class you can define a dynamic type so,
public class Box<T>
The same way to do this in a method is like this:
public <U> void test()
The different between this is the place of the "Type Parameter", but in general, is the same.
Correct me if I'm wrong, please.
Next step... A little bit more complicate:
public class Box<T> {
private T variable;
....
public <U> void test(){
U variable = null;
...
One thing that confused me, is that I never used a parameterised method, only like this:
public class Box<T> {
private T variable;
public void test(T variable){
...
On this way I never needed to use in a method.
Now something more complicate. I don't understand why this below code works. Is a very strange example, I know, but is because I can't see it.
public class TestBox<Mike> {
public static void main(String[] args) {
TestBox<Charles> xb = new TestBox();
}
public static <Peter> void get(TestBox u){
}
}
This words, every time are... ¿¿suggestions?? I don't have any class call "Mike" or "Peter"
<Mike>
<Peter>
The previous code compile if I left . I guess that is because If I put something between <> might be a real type.
Then I need to confirm, that the word after static(or public if isn't a static method) is just a "SUGGESTION".
Thanks!!
===================== EDIT 4 =============================
New Question related with previos doubt.
public <String> void testClass(String[] array){
System.out.println("** public <String> void testClass(String[] array){");
}
public void testClass(String[] array){
System.out.println("** public void testClass(String[] array){");
}
Why if I change in the first Method the word String for Integer, I have error...?
Integer is "WhatEverIWant"
public <Integer> void testClass(String[] array){
System.out.println("** public <Integer> void testClass(String[] array){");
}
And Why if I change the parameter String[] for Integer[], it works again?
public <Integer> void testClass(Integer[] array){
System.out.println("** public <Integer> void testClass(String[] array){");
}
I think is ilogical, respect before comments, Ufff...
<T> is not a return type, it's a declaration of generic type parameter T, and it may appear before the return type.
In public <T> T[] toArray(T[] array)
T[] is the return type, and it means that toArray accepts a generic array and returns a generic array of the same type.
In <String> List<String> toArray(String[] array), <String> is a generic type parameter (List<String> is the return type), which is confusing, since it hides the java.lang.String class.
It's completely equivalent to <T> List<T> toArray(T[] array).
EDIT:
public static List<String> testClass(String[] array)
is a method that accepts a String array (i.e. an array whose element type is java.lang.String) and returns a List of Strings.
public static <String> List<String> testClass(String[] array)
is a method that accepts an array of some reference type (it has a generic type parameter called String, which has no relation to java.lang.String) and returns a List of the same type.
The two methods have a different argument, even though it looks like they don't.
When you call testClass with a String[] argument, the non-generic method that accepts a String array will be called, since its arguments are a better fit to the array you are passing than the generic method. If you remove the non-generic method, the generic method is called instead, since the generic method accepts any non-primitive array.
Maybe it will help you better understand if you try to call the method with different types of arrays :
String[] strarray = {"a","b"};
testClass(strarray); // calls the first (non generic) method
Integer[] intarray = {1,2,3,4};
testClass(intarray); // calls the second (generic) method
If you edit your test code in a IDE like 'idea' or others, you will see the color of 'String' is different between first method and second. That means the compiler treat the first 'String' not 'java.lang.String'. It is just a word like T,E or any other word.I think maybe Java should let compiler check the generic type word to avoid confused with Java's keywords.
Related
I'm having trouble understanding why I'm getting a compilation error here. Let me share some simple code. The following block of code works fine:
public class Test {
public static void main(String[] args) {
String[] arr = new String[0];
MethodA(arr);
}
public static <E> void MethodA(E[] array) {
Integer[] intArray = new Integer[0];
MethodB(array, intArray);
}
public static <E> void MethodB(E[] array, E[] secondArray) {
//Stuff
}
}
The problem arises when I add a new generic List parameter to MethodB, calling it from MethodA:
public class Test {
public static void main(String[] args) {
String[] arr = new String[0];
MethodA(arr);
}
public static <E> void MethodA(E[] array) {
Integer[] intArray = new Integer[0];
List<E> someList = new ArrayList<E>();
MethodB(array, intArray, someList);
}
public static <E> void MethodB(E[] array, E[] secondArray, List<E> list) {
//Stuff
}
}
Which gives me the following error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method MethodB(E[], E[], List) in the type Test is not applicable for the arguments (E[], Integer[], List)
It seems to be telling me to change the parameter from E[] to Integer[], which is weird because it did not complain about such a thing until after I introduced the List parameter. Again, I feel like I must be making a silly mistake somewhere, but I can't figure it out. Any help would be appreciated! Thanks!
In the first example, you're calling MethodB with a String[] and an Integer[].
Since arrays are "covariant" - meaning, for example, you can cast a String[] to an Object[], it calls the version of MethodB with Object for E.
In the second example, it's similar, but you also have a List<E>. Generic classes do not work the same way of arrays - you cannot cast a List<String> to a List<Object>. So it would be invalid for E to be Object (or anything other than whatever E is in MethodA) since then the third parameter couldn't be converted, and it would also be invalid for E to be String since then the first parameter couldn't be converted. So there is no type that works for E.
Note: If you changed String to Integer in main, it still wouldn't compile, even though E could be Integer. That's because the compiler doesn't know that MethodA is never called with anything other than Integer.
In method B declaration You use the same generic type(E) for all three parameters.
It meens that you may use a parameter of any kind (E) but it must be the same for all 3 parameters.
Try adding another generic type(T) like this:
public class Test {
public static void main(String[] args) {
String[] arr = new String[0];
MethodA(arr);
}
public static <E> void MethodA(E[] array) {
Integer[] intArray = new Integer[0];
List<E> someList = new ArrayList<E>();
MethodB(array, intArray, someList);
}
public static <E, T> void MethodB(E[] array, T[] secondArray, List<E> list) {
//Stuff
}
}
Or if there is a need you can add third so that List does not require the E to be the same type as in E[] array.
MethodB requires all three parameters be of same type. But you are calling it with E and Integer. Try E[] intArray = null; and compiler will not compalin
Hi I'm doing revision for an upcoming test and I've hit a problem I'm having trouble with.
I have to modify this code http://pastebin.com/ED2A7VWy to give a generic implementation of Queue. The problem is that the Queue uses an array and for some reason the generics don't seem to play nicely with arrays. I've tried:
public class Queue<E>{
private E[] eArray = new E[5];
...
}
but that doesn't seem to work.
Cast the array instead :
#SuppressWarnings("unchecked")
private E[] eArray = (E[])new Object[5];
You can read here why it is not allowed.
You can do this - no suppressing of warnings - no casting:
public class Objects {
// Call without a second parameter to get an array of the specified type with the specified length.
public static <T> T[] newArray(int length, T... empty) {
return Arrays.copyOfRange(empty, 0, length);
}
}
public class Test<T> {
public void test() {
// Only specify the length - Java creates an empty one for the varargs param.
T[] demo = Objects.<T>newArray(5);
System.out.println(Arrays.toString(demo));
}
I am testing some small practicals with JAVA generics concept.
I try to return a List from a function that is generic but compile does not allow me to do so.
Check the code below :
package com.test.generic.method;
import java.util.ArrayList;
import java.util.List;
public class Sample_3<T> {
public <T> List<T> testReturn(T t) {
List<T> list = new ArrayList<T> ();
list.add(t);
return list;
}
public static void main(String a[]) {
String s = "Gunjan";
Sample_3 sample = new Sample_3<String>();
List<String> list =(List<String>) sample.testReturn(sample);
for(String ab : list){
System.out.println(ab);
}
}
}
It gives ClassCastException.
How can I return the list from the generic function ?
And why JAVA has added such compile time feature ?
Thanks,
Gunjan.
The purpose of the generics is to allow you freedom to specify. So you can do something like:
public List<T> testReturn(T t){
List<T> list = new ArrayList<T>();
list.add(t);
return list;
}
which would then allow you to do e.g.
List<String> a = testReturn("A");
List<Integer> b = testReturn(1);
The issue with your code is that the intent of your method isn't really generic - the method is written to return a List<String>. The <T> in your code doesn't actually affect anything.
In response to your further question -
You've written the following:
public class Sample_3 {
public <T> List<T> testReturn(T t) {
List<T> list = new ArrayList<T> ();
list.add(t);
return list;
}
public static void main(String a[]) {
String s = "Gunjan";
// this is wrong - the Sample_3 class is not generic. It does not get a generic type parameter.
Sample_3 sample = new Sample_3<String>();
// this is also wrong. Since the method is generic the "T" you get is the same as the "T" that
// goes into the method. Here your String output type does not match the input type of the method,
// which you've set here as a Sample_3 object
List<String> list =(List<String>) sample.testReturn(sample);
}
Your generic parameter T sets the type that can vary. YOu can do it onthe method level, as above, or on the class level as well:
public class Sample_3<T> {
public List<T> getList(){ ... }
}
e.g.
List<Integer> l = new Sample_3<Integer>().getList();
List<String> s = new Sample_3<String>().getList();
List<Calendar> c = new Sample_3<Calendar>().getList();
You need to make the signature:
public List<String> testReturn()
When you use <T> in a method declaration it tells the compiler you want to use a generic type in the method based on one of the parameters passed in. Because you are not passing in any parameters with type T, the compiler cannot resolve T to a type.
You are specifying a return type of a list of T, but returing a list of String. The quick fix is:
public List<String> testReturn(){
List<String> list = new ArrayList<String>();
// add stuff to list;
return list;
}
Really, the purpose of generics is to allow you to generify your class (i.e. Sample) so that you can create new instances of it, but specify what kind of objects it handles for each instance. Java checks these generics at compile time. Take List as an example - without generics, it doesn't care about what Objects I put into the list - which means I might put a Double into without getting a warning, and I don't know what kind of Object it contains when I call get(). We could write new classes or implementations of List that are limited to particular Objects - e.g. a StringList could only accept Strings - but that would be time-consuming and potentially difficult to maintain. Instead, Java generics allows us to restrict the input to / output from a particular List to a particular class (and any implementations of that class) - e.g. List only accepts and returns Strings.
e.g. to generify Sample:
public class Sample<T> {
public List<T> makeEmptyList(){
return new ArrayList<T>();
}
}
You can also specify generics in static methods, but you have to pass the generic type in - either as an object or a class reference:
public static <T> List<T> makeAndAddToList(T t){
List<T> list = new ArrayList<T>();
list.add(t);
return list;
}
public static <T> List<T> makeEmptyList(Class<T> clazz){
return new ArrayList<T>();
}
In your case, it's not clear whether your Sample classes should be restricting to handling Strings and List or whether it should be made generic.
For further info, check out the Java tutorial.
I think the first question to ask is, why are you declaring the method to return List<T> when you are explicitly creating a List<String> within the method? That example isn't going to buy you anything by using generics. Is there some other scenario that you are trying to solve?
Just to give you a quick lesson, when Java compiles your classes, generics are removed from the bytecode. So, what may initially look like this in your editor:
public <T> List<T> testReturn(){
}
public List<String> testReturn2(){
}
Looks like this once compiled to bytecode:
public List testReturn(){
}
public List testReturn2(){
}
This is known as type erasure. The purpose of generics is to allow for compile-time parameter checking, thereby removing exceptions at runtime. An example of non-generic code that will produce a runtime error is the following:
List list = new ArrayList();
list.add("Hello World");
list.add(Integer.valueOf(42));
for (int i = 0; i < list.size(); i++) {
String str = (String) list.get(i);
}
The String and the Integer placed in the List are incompatible types. Generics prohibit this kind of unsafe access/data entry (though it will always believe you when you cast something).
A generic 'display()' method would look like the following:
public static void main(String a[]) {
List<String> list = new ArrayList<String>();
list.add("Gunjan");
list.add("Shah");
new Sample_2().display(list);
}
public <T> void display(List<T> list) {
for (T t : list) {
System.out.println(t);
}
}
When using generics in a generic method, you must reference your generic values as though they are of an object type named, T
I think you are putting String objects in the List, then make the List of type String.
public List<String> testReturn(){
List<String> list = new ArrayList<String>();
list.add("Gunjan");
list.add("Parth");
list.add("Devang");
return list;
}
Generics can be used with class, methods, variable, But its most important use it to make the Collections more Type Safe
<T> is called Type Parameters, if this is used in the method, then it means that, this is the Type to be used with in the method when it encounters a parameter with type T.
I have a class as follows:
public class MyConverter {
public <T> T convert (Object o, String typeidentifier, T dummy)
{
... do some conversions such as a java array to an ArrayList or vice versa
... based on a typeidentifier syntax similar to Class.getName() but which
... embeds information about generic subtypes
}
}
and want to be able to do something general like this:
int[] ar = {...};
ArrayList<Integer> dummy = null;
Integer elem = MyConverter.convert(ar, "java.util.ArrayList<Integer>", dummy)
.get(15);
That is, the T in convert may itself be a generic instance, and I found that to get this goal to work, I have to pass a fully typed dummy, as ArrayList.class won't give the java compiler enough information that it is an ArrayList<Integer> if I used Class<T> dummycls instead of T dummy.
Am I missing something? Is there a way to both write and invoke convert without requiring a dummy?
Specify the type on your call, rather than letting java infer the type:
Integer elem = MyConverter.<ArrayList<Integer>>convert(ar, "java.util.ArrayList<Integer>");
This link describes this (cool) syntax.
This kind of looks like Arrays.asList, it will take a native array and convert it to an ArrayList.
An implementation could like the following:
public static <T> List<T> asList(T... a) {
ArrayList<T> arr = new ArrayList<T>();
for (T item: a) {
arr.add(item);
}
return arr;
}
You can write the logic whatever you want to convert in convert method and cast it to generic type and return.
public class MyConverter {
public static void main(String[] args) {
Integer i = MyConverter.convert();
System.out.println(i);
}
#SuppressWarnings("unchecked")
private static <T> T convert() {
Integer i = new Integer(10);
return (T)i;
}
}
Suppose I've got a method that accepts an array and processes each element in it using Java's built in for-each loop, like this:
public static void myFun(SomeClass[] arr) {
for (SomeClass sc : arr) {
// Stuff is processed here
}
}
This works just fine, but now I want to be able to pass the same method a List<SomeClass> instead. Am I destined to use Collection.toArray(T []), or is there a parameter I can use for myFun() that accepts any type that can be used in a for-each construct?
To clarify: I want a method signature that will accept any iterable object, be it a primitive array or a Collection. I can very easily write two methods, with one wrapping the other, but I'm just curious if there's a better way.
I would suggest using Iterable, Collection or List as the parameter type.
IMO, collections should be preferred to reference arrays. If you happen to have an array Arrays.asList does the conversion nicely. Arrays.asList allows gets and sets back through to the array, but obviously not "structural" modifications which would change the array length.
myFun(Arrays.asList(arr));
You may have to use wildcards in extreme/general cases.
public static void myFun(Iterable<? extends SomeClass> somethings) {
for (SomeClass something : somethings) {
// something is processed here
}
}
It is noteworthy that Collections.toArray and Arrays.asList work slightly differently. asList keeps the original array to back the collection, so changes to the collection will be reflected in the array. Collections.toArray makes a (shallow) copy of the collection data. Making a copy is often what you would want anyway if you are returning an array. Asymmetrically, if you are passing as an argument you generally do not copy (unless storing as a field).
Use Iterable. That's what it's for.
As you said, Iterable won't handle arrays.
You don't want to use multiple methods wrapping each other. That rules out Arrays.asList and Collection.toArray.
So the answer to your question is no, there isn't a way. But if you can use Lists, why would you ever use arrays?
I would still go with Iterable here. I like it better than Collection because in the past I've had classes that implemented Iterable but were not collections; this made it easy for them to lazily retrieve values as needed, and I could use the foreach loop on them.
you cannot, java Arrays doesn't implements Iterable:
public static int sum(Iterable<Integer> elements) {
int s = 0;
for (int i : elements) {
s += i;
}
return s;
}
public static void main(String[] args) {
L1: System.out.println(sum(1,2,3));
L2: System.out.println(sum(Arrays.asList(1,2,3)));
L3: System.out.println(sum(new int[] { 1,2,3 }));
}
this results in two compile-time errors in (L1 and L3); so you must design your
method to accept an Iterable (Collections) and/or an Array, at least one method must perform some conversion (to/from array)
WORKAROUND:
you may be try with an adapter:
public class ArrayIterator<T> implements Iterator<T> {
private final T[] array;
private int i;
public ArrayIterator(T[] anArray) {
array = anArray;
i = 0;
}
public boolean hasNext() {
return i < array.length;
}
public T next() {
return array[i++];
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private static int sum(final Integer ... elements) {
return sum(new Iterable<Integer>() {
public Iterator<Integer> iterator() {
return new ArrayIterator<Integer>(elements);
}
});
}
you should pay attention only when dealing with primitive arrays; when you use only
reference object (your case) ArrayIterator + anonymous class are cool
hope it helps
Short answer: no, there's no single method signature that type-safely accepts both an Iterable and an array. Obviously you could just accept Object, but that would be a hack as well.
Long-ish answer: Since the enhanced for-loop is effectively defined twice (once for arrays and once for Iterable), you'll need to provide two overloaded methods as well:
public static void myFun(SomeClass[] array) {
for (SomeClass sc : array) {
doTheProcessing(sc);
}
}
public static void myFun(Iterable<? extends SomeClass> iterable) {
for (SomeClass sc : iterable) {
doTheProcessing(sc);
}
}
Although the source of the two methods looks exactly the same, you'll need to define it twice (unless of course you wrap the Array in your own Iterable as #dfa outlined).
There's a little know feature of Java Generics in Java 1.5+ where you can use <? extends Subtype> in your method calls and constructors. You could use <? extends Object>, and then anything that deals with those would have access only to methods on Object. What you might really want is something more like this:
List<? extends MyCrustaceans> seaLife = new ArrayList<? extends MyCrustaceans>();
MyShrimp s = new MyShrimp("bubba");
seaLife.add(s);
DoStuff(seaLife);
...
public static void DoStuff(List<? extends MyCrustaceans> seaLife)
{
for (MyCrustaceans c : seaLife) {
System.out.println(c);
}
}
So if you have a base class (like MyCrustaceans), you can use any methods of that base class in DoStuff (or of the Object class, if your parameter is <? extends Object>). There's a similar feature of <? super MyType>, where it accepts a parameter that is a supertype of the given type, instead of a subtype. There's some restrictions on what you can use "extends" and "super" for in this fashion. Here's a good place to find more info.
public static void myFun(Collection<MyClass> collection) {
for (MyClass mc : collection) {
// Stuff is processed here
}
}