I am currently new to working with Java. So far I have been able to easily use the basic such as Classes, Functions, Arrays etc from my knowledge of JavaScript and PHP.
However, what I have never seen before is this: <>. Inside of that is a variable of some type. Because I don't know what it is called, I can't find any answers.
I've seen them like this:
List<String> myList = new ArrayList<String>();
But also like:
public static <T> boolean contains( final T[] array, final T v ) {
for ( final T e : array )
if ( e == v || v != null && v.equals( e ) )
return true;
return false;
}
What does the <String> mean?
In the function, I was also wondering what is specifically special about the T?
This is for a Generic type
What it allows you to do is to pass through a type, and make it useful for multiple object types
So List is a generic collection, and it allows you to make a list of any object. Making List<String> will make the object be a list of Strings. Or you could use List<MyClassType> and it would make a list of objects of your class
That defines the Type of the objects a collection can hold
so when you write List<String> myList = new ArrayList<String>();, it means
Create an arrayList that can hold the String objects.
SomeParameterizedClass{
T someValue;
}
means that you can pass your type to the class e.g. SomeParameterizedClass<String> so that someValue becomes of type String
When you write List<T> list = new ArrayList<T>();, here T can be any type.
With java 7 you can simply say List<String> list = new ArrayList<>();. It has the same meaning.
In java 7 <> is called as diamond operator
You will find < > in the following cases, the T is a "parameter type" in plain English that means you could "substitute" that T by any other non-primitive type (like String or Integer):
When you are declaring a parameterized Type:
class MyParameterizedClass<T> {
public T myValue;
}
When you are declaring variables of your parameterized type:
MyParameterizedClass<String> myStringParam;
MyParameterizedClass<Integer> myIntegerParam;
When you are using constructors of parameterized types:
MyParameterizedClass<String> myStringParam = new MyParameterizedClass<String>();
MyParameterizedClass<Integer> myIntegerParam = new MyParameterizedClass<Integer>();
myStringParam.myValue = "Hello world";
myIntegerParam.myValue = 5;
When you declare a generic method:
public <T> T updateMyValue(T myValue) {
this.myValue = myValue
}
What you have observed is partially correct! In Java, inside <> should be 'Object' type or any subclass type of 'Object'.
When you declare myList as below:
List<String> myList = new ArrayList<String>(), it ensures the declared and initialized myList has elements of only String type. This is called type declarations.
public static <T> boolean contains( final T[] array, final T v ) { }, is a generic method declaration which implies that the passed two parameters should be of same object type, 1st parameter an array, 2nd parameter an object. And the method return type also should be same type.
Refer Java docs for more examples on Generics. http://docs.oracle.com/javase/tutorial/extra/generics/methods.html
Related
I am new to Java. In this document they give this as a use case for using wildcard:
static void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
This is their solution:
static void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
But I could do the same without a wild card:
static <T> void printCollection(Collection<T> c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
Can someone show me a simple use case where regular generics won't work but a wild card will?
Update: The answers over here When to use wildcards in Java Generics? do NOT tell us the need for wildcard. In fact its the other way around.
One thing wildcards allow us to do is declare types that are agnostic towards a particular type parameter, for example a "list of any kind of list":
List<List<?>> listOfAnyList = ...;
listOfAnyList.add( new ArrayList<String>() );
listOfAnyList.add( new ArrayList<Double>() );
This is impossible without a wildcard:* because the element lists may have different types from each other.
And if we try to capture it, we will find that we can't:
static <E> void m(List<List<E>> listOfParticularList) {}
m( listOfAnyList ); // <- this won't compile
Another thing wildcards allow us to do that type parameters cannot is set a lower bound. (A type parameter can be declared with an extends bound, but not a super bound.**)
class Protector {
private String secretMessage = "abc";
void pass(Consumer<? super String> consumer) {
consumer.accept( secretMessage );
}
}
Suppose pass was instead declared to take a Consumer<String>. Now suppose we had a Consumer<Object>:
class CollectorOfAnything implements Consumer<Object> {
private List<Object> myCollection = new ArrayList<>();
#Override
public void accept(Object anything) {
myCollection.add( anything );
}
}
The problem is: we can't pass it to a method accepting Consumer<String>. Declaring Consumer<? super String> means that we can pass any consumer which accepts a String. (Also see Java Generics: What is PECS?.)
Most of the time, wildcards just let us make tidy declarations.
If we don't need to use a type, we don't have to declare a type parameter for it.
* Technically also possible with a raw type, but raw types are discouraged.
** I don't know why Java doesn't allow super for a type parameter. 4.5.1. Type Arguments of Parameterized Types may hint that it has something to do with a limitation of type inference:
Unlike ordinary type variables declared in a method signature, no type inference is required when using a wildcard. Consequently, it is permissible to declare lower bounds on a wildcard […].
T stands for the generic type of that data structure. In your last example, you don't use it, and its NOT an actual type (for example String), and because you don't use it it doesn't really matter in this case.
For example, if you had a Collection and tried to pass it to a method that accepts a Collection, that works because there is no type T on the classpath so its considered a variable. If you tried passing the same Collection to a method that accepts a Collection, that would not work because you have String on your classpath so its not a variable.
Take List as the example.
List<?> can be the parent class of List<A>.
for instance,
List<B> bList = new ArrayList<>(); // B is a class defined in advance
List<?> list = bList;
you can never use <T> in this situation.
<?> has the wildcard capture.
here,
void foo(List<?> i) {
i.set(0, i.get(0));
}
the code above cannot be compiled. You can fix it:
void foo(List<?> i) {
fooHelper(i);
}
// wildcard can be captured through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
see more, http://docs.oracle.com/javase/tutorial/java/generics/capture.html
I can only think of the two currently, later may update.
I am writing a function called arrayToMap(). I was originally going to just allow String arrays, but I thought I could make it generic so any type of array. I tried the following code snippet but it tells me T cannot be resolved to a type:
public Map <T, Integer> arrayToMap( T [] arr ) {
assert( arr != null ) : "arrayToMap null array";
Map<T,Integer> res = new HashMap<>();
int ind = 0;
for ( T val: arr ) {
res.put(val, ind);
ind++;
}
return res;
}
What is the correct syntax?
The signature should be changed to this:
// Notice the <T>, this is a generic method declaration
public <T> Map <T, Integer> arrayToMap( T [] arr )
This is called a generic method and you can read more about it here. Just like generic type declarations (classes and interfaces), method declarations can also be generic which means that they can be parameterized by one or more type parameters such as the <T> in the example above.
However, if your class is a generic class i.e. it is declared as a generic type the signature can stay the same as in the original example.
// A generic type declaration <T>
public class MyClass<T> {
// The type T comes from the generic type declaration,
// therefore the generic method declaration is not needed
public Map<T, Integer> arrayToMap(T [] arr) {
...
}
}
But, a generic class is likely not a good approach for the OP:s original use case since the arrayToMap method kind of implies that it is a general method that can be used on any type of array.
I have the following code
public class Container<T> {
private T element;
private T[] tarray;
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
public void add(T element) {
tarray[0] = element;
}
public void CreateArray(int size) {
//Can this be cleaned up better?
tarray = (T[]) new Object[size];
}
public T get() {
return tarray[0];
}
public Container(T someElement) {
this.element = someElement;
}
public Container() {
}
public static void main(String[] args) {
Container<String> myContaier1 = new Container<String>();
myContaier1.setElement("Hello");
myContaier1.CreateArray(1);
myContaier1.add("GoodBye");
System.out.println(myContaier1.get());
}
}
Is there no way to initialize a type safe generic array?
There is no way unless you provide a reified T in the form of an actual Class<T> object that represents a specific value of T. This is because the array type is reified, whereas the Generic type isn't.
There are two problems here:
First of all, the actual type of your array will always be Object[]. You cast it to T[], but this works only because T[] erases to Object[]. If your class definition said, for example, <T extends Number>, then (T[])new Object[] would fail with a ClassCastException.
You could get around this by passing Class<T> to the constructor of your collection and keeping it in a field:
private Class<T> componentClass;
...
tarray = (T[]) Array.newInstance(componentClass, size);
Now the actual in-memory type of tarray is T[], but you still get an unchecked cast error. Even though you have the component class, as far as I know there is no equivalent of Class.cast() for doing a checked cast of an array instance.
You can do private T[] tarray;, But you cannot assign it to (T[]) new Object[size];. How can an array of Object be same an array of any other class.
T is not even there after compilation.
It is called type erasure. E.g if you do
List<Person> persons = new ArrayList<Person>();
It becomes List persons = new ArrayList() after compilation.
It is similar to ask "Is there a way to initialize a generic object: new T()?"
Of course it is impossible, as the compiler does not know what the type is of it.
Array is the same, its type relies on its elements. If the type of its elements is unknown, the type of itself is unknown.
Those classes which can take generic types like List, Set, Map, etc. are different. They have their own classes as types and they are dynamic, so you can initialize one like new ArrayList<T>().
You can try on this:
public class Test {
public static void main (String args[]) {
int[] i = new int[3];
short[] s = new short[4];
ArrayList<String> a = new ArrayList<String>();
System.out.println(i.getClass().getName());
System.out.println(s.getClass().getName());
System.out.println(args.getClass().getName());
System.out.println(a.getClass().getName());
}
}
You will see the types of elements are already combined with the arrays, while not combined with ArrayList.
There are way to do this on the JVM, but to do something like this in Java would require writing a lot of boiler plate code. In Scala, you can use Manifests to get around type erasure, and can instantiate generic arrays without casting.
An example:
class Container[T : Manifest]() {
def createArray(size:Int) = Array.ofDim[T](size)
}
scala> val f = new Container[String]
f: Container[String] = Container#12f3aa66
scala> f.createArray(5)
res7: Array[String] = Array(null, null, null, null, null)
Scala code compiles to the same bytecode class files as Java (implying that this should be possible in Java if you jumped through enough hoops and maybe wrote your own manifests). Scala classes can be imported into Java projects... though I'm not sure how hard it is to instantiate a class like this from Java.
If you often find yourself wanting to be able to handle more complicated type situations, have a look at Scala, you might like it.
Is it possible to obtain an instance of Class<SomeGenericClass<SomeType>>?
For example an instance of Class<ArrayList<String>> without instantiating an instance of ArrayList<String>.
#SuppressWarnings("unchecked")
public static <T> T castToGeneric(Object objectToCast, T typeInferrenceClass) {
return (T)objectToCast;
}
// usage
Object someObject = null /* initialised elsewhere */;
List<String> listOfStrings = castToGeneric(someObject, new ArrayList<String>());
// I want to avoid having to create - new ArrayList<String>();
I know this code is ugly and I shouldn't be doing this. But we are where we are...
I found this workaround to your problem:
#SuppressWarnings("unchecked")
public static <T> T castToGeneric(Object objectToCast) {
return (T)objectToCast;
}
// usage
Object someObject = null /* initialised elsewhere */;
Class<ArrayList<String>> listOfStrings = <Class<ArrayList<String>>>castToGeneric(someObject);
so instead of passing type as a parameter, you're passing it as a type-parameter.
If want to instantiating Class<SomeGenericClass<SomeType>>
Class<List<Object>> list = (Class<List<Object>>)(Class<?>)List.class;
Type genericclass = new ArrayList<Object>() {}.getClass().getGenericSuperclass();
Since type erasure in Java generic, result of call "getClass" on instance of "List<String>", would lose type argument "String", in another word, it might be impossible to get "Class<List<String>>". But you can do it in Scala:
scala> List("foo", "bar").getClass
res0: java.lang.Class[_ <: List[java.lang.String]] = class scala.collection.immutable.$colon$colon
This satisfies your "no-instance" criterion, compiles and runs OK:
Class<ArrayList<String>> c =
(Class<ArrayList<String>>) Class.forName("java.util.ArrayList");
This works due to type erasure: At runtime, ArrayList<String> is just ArrayList, and further Class<ArrayList<String>> is just Class.
In Java it is possible to declare an array of type variables, but I'm not able to create the array. Is it impossible?
class ClassName<T> {
{
T[] localVar; // OK
localVar = new T[3]; // Error: Cannot create a generic array of T
}
}
Generic type of array are not there in Java.
You can go for ArrayList
Explanation :
array in java are of covariant type.
Java arrays have the property that there types are covariant , which means that an array of supertype references is a supertype of an array of subtype references.That is, Object[] is a supertype of String[] for example. As a result of covariance all the type rules apply that are customary for sub- and supertypes: a subtype array can be assigned to a supertype array variable, subtype arrays can be passed as arguments to methods that expect supertype arrays, and so on and so forth.Here is an example:
Object[] objArr = new String[10];// fine
In contrast, generic collections are not covariant. An instantiation of a parameterized type for a supertype is not considered a supertype of an instantiation of the same parameterized type for a subtype.That is, a LinkedList<Object> is not a super type of LinkedList<String> and consequently a LinkedList<String> cannot be used where a LinkedList<Object> is expected; there is no assignment compatibility between those two instantiations of the same parameterized type, etc.Here is an example that illustrates the difference:
LinkedList<Object> objLst = new LinkedList<String>(); // compile-time error
Source: http://www.angelikalanger.com/Articles/Papers/JavaGenerics/ArraysInJavaGenerics.htm
T[] localVar = (T[])(new Vector<T>(3).toArray()); // new T[3];
This is only possible in a language with reified generics, like Gosu. Java has type erasure, so the type of T isn't available at runtime.
You can't. You can do it if you have the Class object representing T, you can use java.lang.reflect.Array:
public static <T> T[] createArray(Class<T> clazz, int size) {
T[] array = (T[]) java.lang.reflect.Array.newInstance(clazz, length);
return array;
}
Another way that I got around this is by creating a different class to hold the type-Variable, then create an array of that class
eg.
public class test<T>{
data[] localVar = new data[1];
private class data<E>{
E info;
public data(E e){ info = e; }
}
public void add(T e){ localVar[0] = new data<T>(e); }
}
the code above cant be used for anything practical unless you want to add one item to an array, but its just shows the idea.
You cannot initialize a generic type directly. However, you can create an array of Object type and cast it to generic array type as following:
class ClassName<T> {
{
T[] localVar = (T[]) new Object[3];
}
}