Generics

                                                                        Generics

1. 

public class Lists {

// Here is a method that accepts an array of any type and converts it to a list
public static <T> List<T> toList(T[] arr) {
List<T> list = new ArrayList<>();
for (T elt : arr) list.add(elt);
return list;
}

public static <T> List<T> toList2(T... arr) {
List<T> list = new ArrayList<>();
for (T elt : arr) list.add(elt);
return list;
}

public static <T> void addAll(List<T> list, T... arr) {
for (T elt : arr) list.add(elt);
}

} 

2. 

List<Number> nums = new ArrayList<>();
List<Integer> ints = Arrays.asList(1, 2);
List<Double> dbls = Arrays.asList(2.2, 3.3);
nums.addAll(ints);
nums.addAll(dbls);
System.out.println(nums);
Number number = nums.get(0);
Number number1 = nums.get(2);
System.out.println(number);
System.out.println(number1);

3. 

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3); // compile-time error
nums.add(3.12); // compile-time error

Compile-time error ona gore baw verir ki, List<? extends Number> bawa duwmur ki, one gonderilen deyer Integerdi yoxsa Doubledir. Mes: hansisa developer awagidaki kimi kod yaza biler:

public class Main {
public static void main(String[] args) {
List<Integer> ints = Arrays.asList(1, 2);
List<? extends Number> nums = ints;
nums.add(2);
List<Double> doubles = Arrays.asList(2.2, 3.3);
foo(nums, doubles);
}

public static void foo(List<? extends Number> numbers, List<Double> doubles) {
numbers.add(doubles.get(0));
}
}


In general, if a structure contains elements with a type of the form ? extends E, we can

get elements out of the structure, but we cannot put elements into the structure. To

put elements into the structure we need another kind of wildcard, as explained in the

next section.

4. Here is a method that copies into a destination list all of the elements from a source list, from the convenience class Collections:

public class Main {
public static void main(String[] args) {
List<Object> objects = Arrays.asList(2, 3.3, "four");
List<Integer> ints = Arrays.asList(5, 6);
copy(objects, ints);
}

public static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
}

Burada, T artiq ink gonderilen arqument olur, yeni T = Object, ve Integer Objectden extend etdiyi ucun

ikinci arqumenti qebul edir.

5. 







I will try to explain Generics using examples.

0.General.

package main;

public class Container {
private Object object;

public Container(Object object) {
this.object = object;
}

public Object doSomething() {
System.out.println("I did something");
return this.object;
}
}
package main;

public class Main {
public static void main(String[] args) {
Container container = new Container(new User());
doIt(container);
}

public static void doIt(Container container) {
User u = (User) container.doSomething();
System.out.println("u = " + u);
}

public static void foo(RandomClass<? extends User> randomClass) {
RandomClass<User> random = (RandomClass<User>) randomClass;
}
}
This look normal. Because we can downcast User u. 
package main;

public class Main {
public static void main(String[] args) {
Container container = new Container(new String("somehting"));
doIt(container);
}

public static void doIt(Container container) {
User u = (User) container.doSomething();
System.out.println("u = " + u);
}

public static void foo(RandomClass<? extends User> randomClass) {
RandomClass<User> random = (RandomClass<User>) randomClass;
}
}
But here we will get (runtime) ClassCastException. Because it is imposible to cast String to
User. And it turns out that our program is not Type Safe. And to prevent it we use Generics.

1.Generics belonging to class.

package main;

public class Container<T> {

private T object;

public Container(T object) {
this.object = object;
}

public T doSomething() {
System.out.println("I did something");
return this.object;
}
}
package main;

public class Main {
public static void main(String[] args) {
Container<User> container = new Container<>(new User());
doIt(container);
}

public static void doIt(Container<User> container) {
User u = container.doSomething();
System.out.println("u = " + u);
}

public static void foo(RandomClass<? extends User> randomClass) {
RandomClass<User> random = (RandomClass<User>) randomClass;
}
}
As we see this is Type Safe and we do not need in the method doIt to cast User.

Another interesting fact:
package main;

public class Container<T> {

public T doSomething() {
return null;
}

}
package main;

public class Main {
public static void main(String[] args) {
Container container = new Container();
User user = container.doSomething();
}
}
If we do not declare in diamond bracket to which class container belongs, that means it 
belongs automatically to Object. So we must explicitly cast User, otherwise it is compile time 
error. 

Here I have found interesting fact:
package main;

public class Main {
public static void main(String[] args) {
Container container = new Container();
foo(container.doSomething());
foo(null);
}

public static void foo(Object object) {
System.out.println("object");
}

public static void foo(User user) {
System.out.println("user");
}

public static void foo(User2 user2){
System.out.println("user2");
}
}
Outcome:
user2
user2
But why? Because according to hierarchy User2 the latest version.


Another example:
package main;

public class User<A> {
public String name = "user";
protected A apple;
}
package main;

public class User2 extends User<String > {
public String name = "user2";

public String method(){
return apple;
}
}
Here User2 class users User class as String. So we see method returns String, and variable
apple is type of String.
2.Type Erasure.
After compilation whatever generics we user the remainder will be Raw Type.
If we try to do decompile .class file to .java file we will get Raw Type.
package main;

public class Main {
public static void main(String[] args) {
Container container = new Container(new User());

}
This is above is Raw Type.
Generics provide us compile time Type Safety.

Another example:
package main;

public class Main {
public static void main(String[] args) {
Container<User> container = new Container(new String());
User user = container.doSomething();
    }
Here we can see IDE doesn't show compile time error. But it will show us runtime error.
That means we should put diamond brackets after declaration of the class.
 

package main;

public class RandomClass<T> {

public T doSomething(T obj){
return obj;
}

public Object foo(Object obj){
return obj;
}
}
package main;

public class Main {
public static void main(String[] args) {

RandomClass<User> randomClass = new RandomClass<>();
String s = (String) randomClass.doSomething(new User());

String s2 = (String) randomClass.foo(new User());
}
}
When we put . after randomClass IDE automatically shows us that this method will serve for only
User class.
package main;

public class Main {
public static void main(String[] args) {

RandomClass<User> randomClass = new RandomClass<>();
String s = (String) randomClass.doSomething(new User());
}
}
And if we try to cast it to the object of another class IDE will not be able to cast it.
However IDE does allow us to cast it to object s2. But in runtime it will throw us 
ClassCastException.

3.If we do not indicate in the diamond brackets to which class generics going to serve, then 
by default it is going to be Object.
package main;

public class Main {
public static void main(String[] args) {
RandomClass randomClass = new RandomClass();
randomClass.doSomething(new User());
}
}
If we hover a mouse over RandomClass we will notice that it say: extends Object.
If we do not put <> diamond brackets when we initialize class it is called Raw type.

4.We can declare several Generics simultaneously.
package main;

public class RandomClass<T, E> {

public T doSomething(T obj1, E obj2){
System.out.println(obj2);
return obj1;
}
}
package main;

public class Main {
public static void main(String[] args) {
RandomClass<User, String> userStringRandomClass = new RandomClass<>();
userStringRandomClass.doSomething(new User(), "Parvin");
}
}

5.Wildcards.
package main;

public class RandomClass<T> {

public T doSomething(T obj) {
return obj;
}

}
package main;

public class User {
public String name = "user";
}
package main;

public class User2 extends User {
public String name = "user2";
}
package main;

public class User3 extends User2{
public String name = "user3";
}
package main;

public class Main {
public static void main(String[] args) {
RandomClass<User2> randomClass = new RandomClass<>();
foo(randomClass);
User2 user2 = new User2();
User user = user2;
}

public static void foo(RandomClass<? extends User> randomClass){
RandomClass<User> random = (RandomClass<User>) randomClass;
}
}
We can only send to foo method objec of classes which extends from User.


package main;

public class Container<? extends Number>{

public <T> T doSomething() {
return null;
}
}
It is impossible to put wildcards on top of the class. However, it is possible to do it in
way below:
package main;

public class Container<T extends Number>{

public <T> T doSomething() {
return null;
}
}
Then when we use container we have to write class than extends from Number(in this example).

6.Upper bound and Lower bound.

Upperbound - Class Worker remains in up level so that Worker is the Upperbound of Programmer
and HR classes.
Lowerbound - the super of class Worker are Person and Object classes so that Worker class 
Lowerbound of these tow classes.


package main;

public class Main {
public static void main(String[] args) {
Container<User2> container1 = new Container<>();
Container<User> container2 = new Container<>();
mehtod1(container1);
method2(container2);
}

public static void mehtod1(Container<? extends User> container) {// upperbound

}

public static void method2(Container<? super User3> container) {// lowerbound

}
}

User in this hierarchy remain on the top and User2, User3, User4 extends from User so that 
User is Upperbound.
User3 here in low part and User2, User on the top so that User3 is Lowerbound.


package main;

public class Container<E> {

private E stored;

public Container(E object) {
stored = object;
}

public E castAndReturn(E passed) {
System.out.println("passed = " + passed.getClass().getName());
System.out.println("stored = " + stored.getClass().getName());
return (E) passed;
}
}
package main;

public class Main {
public static void main(String[] args) {
Container<User2> container = new Container<>(new User2());
method1(container);
}

public static void method1(Container<? super User3> container) {// upperbound
// container.castAndReturn(new User());
// container.castAndReturn(new User2())
container.castAndReturn(new User3());
container.castAndReturn(new User4());
}

First of all we have to sent to the method1 object of classes that User3 extends from them.
For example User2, that is ok. 


package main;

public class Main {
public static void main(String[] args) {
Container<User4> container = new Container<>(new User4());
method2(container);
}

public static void method2(Container<? extends User3> container){
container.castAndReturn(new User());
container.castAndReturn(new User2());
container.castAndReturn(new User3());
container.castAndReturn(new User4());
}

Here to method2 we can send only object which extends from User3. User4 extends User3 so that
it is possible to send User4 to method2. But we can not user container.castAndReturn.
But why? 

Lelt's imagine X and Y separately extends from User. And how is it possible to cast Y to X.
And because of it IDE beforehand does not know whether hierarchically one class extends from 
another or separately.



package main;

public class Container<E> {

private E stored;

public Container(E object) {
stored = object;
}

public E castAndReturn(E passed) {
System.out.println("passed = " + passed.getClass().getName());
System.out.println("stored = " + stored.getClass().getName());
return (E) passed;
}

public E doo(){
return stored;
}
}
package main;

public class Main {
public static void main(String[] args) {
Container<User4> container = new Container<>(new User4());
method2(container);
}

public static void method2(Container<? extends User3> container){
// container.castAndReturn(new User());
// container.castAndReturn(new User2());
// container.castAndReturn(new User3());
// container.castAndReturn(new User4());
User3 user3 = container.doo();
}

Here we are sure that doo will return in our example User4 object and it is possible to 
write User3 user3 = container.doo();



package main;

public class Container<E> {

private E stored;

public Container(E object) {
stored = object;
}

public E castAndReturn(E passed) {
System.out.println("passed = " + passed.getClass().getName());
System.out.println("stored = " + stored.getClass().getName());
return (E) passed;
}
}
package main;

public class Main {
public static void main(String[] args) {
Container<User> container = new Container<>(new User());
method1(container);
}

public static void method1(Container<? super User3> container) {// upperbound
// container.castAndReturn(new User());
// container.castAndReturn(new User2())
container.castAndReturn(new User3());
container.castAndReturn(new User4());
}
}
Here in commented section it is impossible to cast. But Why?

The IDE doesn't know whether the extension is hierarcically or not. Here we see User2 extends
from User but what if it extends from another class. So that it is understandable that classes
which extends from User3 can be easily casted to sent class.

7.Unbound wildcard.
package main;

public class Main {
public static void main(String[] args) {
Container<User> container = new Container<>(new User());
method(container);
}

public static void method(Container<?> container){

}

We can send any type of class objects there but can not cast. Because IDE doesn't know 
which class to cast to which.

Комментарии

Популярные сообщения из этого блога

Lesson1: JDK, JVM, JRE

SE_21_Lesson_11: Inheritance, Polymorphism

SE_21_Lesson_9: Initialization Blocks, Wrapper types, String class