Generics

1. Without generics

package org.example;

public class Container {

private Object object;

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

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

} 

package org.example;

public class User {
private String name;
private String surname;

public User() {
}

public User(String name, String surname) {
this.name = name;
this.surname = surname;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSurname() {
return surname;
}

public void setSurname(String surname) {
this.surname = surname;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", surname='" + surname + '\'' +
'}';
}
}
package org.example;

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

}

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

Bu shekilde olduqda ise problem yaradir, yeni run-time error atir.

package org.example;

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

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


-- Indi ise generic ile yazaq:

package org.example;

public class Container<T> {

private T object;

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

public T doItAndReturn() {
System.out.println("I did something");
return this.object;
}
}
package org.example;

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

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


-- Raw type

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

-- Class cast exception

public static void main(String[] args) {
Container<User> container = new Container(new String());
User u = container.doItAndReturn();
}

Lakin <> diamond operator bizi exceptiondan qoruyur:

public static void main(String[] args) {
Container<User> container = new Container<>(new User());
User u = container.doItAndReturn();
}

Yazilmadiqda onu Object kimi qebul edir.


2. With generics 

package org.example;

public class Container<T extends User> {

private T object;

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

public T doItAndReturn() {
System.out.println("I did something");
return this.object;
}
}
public static void main(String[] args) {
Container<String> container = new Container<>(new String());
}


-- Metodlarda generic

package org.example;

public class Container {

public <T> T doItAndReturn(T object) {
System.out.println("I did something");
return object;
}

public <E> E doItAndReturn2(E object) {
System.out.println("I did something");
return object;
}
}
package org.example;

public class Main {
public static void main(String[] args) {
Container container = new Container();
Double d = container.<Double>doItAndReturn(3.14);
Integer i = container.<Integer>doItAndReturn(3);
}
}


-- Tricky situation

package org.example;

public class Container {

public <T> T doItAndReturn(T object) {
System.out.println("I did something");
return object;
}

public Object foo(User s) {
return s;
}
}
package org.example;

public class Main {
public static void main(String[] args) {
Container container = new Container();
User u = container.doItAndReturn(new User());
User user = container.foo(new User());
}
}


-- 

package org.example;

public class Container {

public <T> T doItAndReturn(String s) {
T t = (T) s;
return t;
}

public Object foo(String s) {
Object o = s;
return o;
}
}
package org.example;

public class Main {
public static void main(String[] args) {
Container container = new Container();
User u = container.doItAndReturn("Hello");
User user = container.foo("Hello");
}
}

Bes neye gore generic iwletdiyimiz yerde bu xetani vermir? Cunki arxa planda ozu cast edir:

User u = (User) container.doItAndReturn("Hello");


-- Another interesting fact:

package org.example;

public class Container {

public <T> T doItAndReturn() {
T t = null;
return t;
}
}
package org.example;

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

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

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


-- One class extends another generic class:

package org.example;

public class User<A> {
private String name;
private String surname;
protected A something;

public User() {
}

public User(String name, String surname) {
this.name = name;
this.surname = surname;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSurname() {
return surname;
}

public void setSurname(String surname) {
this.surname = surname;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", surname='" + surname + '\'' +
'}';
}
}
package org.example;

public class User2 extends User<String>{

public void method() {
String k = something;
System.out.println(k);
}
}


3. Wildcard

Wildcard metodlarda istifade olunur.

package org.example;

public class Container<T> {

public T doItAndReturn(T obj) {
return null;
}
}
package org.example;

public class User {
}
package org.example;

public class User2 extends User{
}
package org.example;

public class User3 extends User2{
}
package org.example;

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

public static void foo1(Container<? extends User2> container) { // upper bound

}

public static void foo2(Container<? super User3> container) { // lower bound

}
}


***

package org.example;

public class Container<T> {

private T stored;

public Container(T obj) {
this.stored = obj;
}

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

public class Main {
public static void main(String[] args) {
Container<User3> container = new Container<>(new User3());
container.castAndReturn(new User4());
container.castAndReturn(new User3());
container.castAndReturn(new User2());
foo1(container);
}

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






------------------------------------------------------------------------------------------------------------------------------

package org.example.example2;

public class MyClassIntegers {
Integer i;

MyClassIntegers(Integer i) {
this.i = i;
}

void printStuff() {
System.out.println(i);
}
}
package org.example.example2;

public class MyClassDouble {
Double i;

MyClassDouble(Double i) {
this.i = i;
}

void printStuff() {
System.out.println(i);
}
}
package org.example.example2;

public class MyClass<T> {
T ob;

MyClass(T ob) {
this.ob = ob;
}

void showType() {
System.out.println(ob.getClass().getName());
}
}
package org.example.example2;

public class Main {
public static void main(String[] args) {
MyClass<Integer> integer = new MyClass<>(10);
MyClass<Double> doubles = new MyClass<>(10.0);

integer.showType();
doubles.showType();
}
}

--

package org.example.example2;

public class MyClass<T, V> {
T ob;
V ob2;

MyClass(T ob, V ob2) {
this.ob = ob;
this.ob2 = ob2;
}

void showType() {
System.out.println(ob.getClass().getName());
System.out.println(ob2.getClass().getName());
}
}
package org.example.example2;

public class Main {
public static void main(String[] args) {
MyClass<Integer, Double> something = new MyClass<>(10, 20.0);
something.showType();
}
}

--

package org.example.example2;

public class NumericFns<T extends Number> {
T ob;

NumericFns(T ob) {
this.ob = ob;
}

double square() {
return ob.intValue() * ob.intValue();
}
}
package org.example.example2;

public class Main {
public static void main(String[] args) {
NumericFns<Integer> iob = new NumericFns<>(4);
System.out.println(iob.square());
}
}

--- a problem

package org.example.example2;

public class NumericFns<T extends Number> {
T num;

NumericFns(T ob) {
this.num = ob;
}

boolean absEqual(NumericFns<T> ob) {
if (Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue()))
return true;
return false;
}
}
package org.example.example2;

public class Main {
public static void main(String[] args) {
NumericFns<Integer> iob = new NumericFns<>(4);
NumericFns<Double> dob = new NumericFns<>(-4.0);

iob.absEqual(dob);
}
}

-- solution

package org.example.example2;

public class NumericFns<T extends Number> {
T num;

NumericFns(T ob) {
this.num = ob;
}

boolean absEqual(NumericFns<?> ob) {
if (Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue()))
return true;
return false;
}
}
package org.example.example2;

public class Main {
public static void main(String[] args) {
NumericFns<Integer> iob = new NumericFns<>(4);
NumericFns<Double> dob = new NumericFns<>(-4.0);

System.out.println(iob.absEqual(dob));
}
}




1. Generic Methods and Varargs

Here is a method that accepts an array of any type and converts it to a list:

public class Lists {

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

The static method toList accepts an array of type T[] and returns a list of type List, and does so for any type T. This is indicated by writing at the beginning of the method signature, which declares T as a new type variable. A method which declares a type variable in this way is called a generic method. The scope of the type variable T is local to the method itself; it may appear in the method signature and the method body, but not outside the method.

List<Integer> ints = Lists.toList(new Integer[]{1, 2, 3});
List<String> strings = Lists.toList(new String[] {"Hello", "World"});
System.out.println(ints);
System.out.println(strings);

--

The vararg feature permits a special, more convenient syntax for the case in which the last argument of a method is an array. To use this feature, we replace T[] with T… in the method declaration:

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

Any number of arguments may precede a last vararg argument. Here is a method that accepts a list and adds all the additional arguments to the end of the list: 

public static <T> void addAll(List<T> list, T... arr) {
for (T elt : arr) list.add(elt);
}
List<Integer> ints = new ArrayList<>();
Lists.addAll(ints, new Integer[] {1, 2});
System.out.println(ints);


2. Subtyping and the substitution principle

According to the Substitution Principle, if we have a collection of numbers, we may add an integer or a double to it, because Integer and Double are subtypes of Number.

List<Number> nums = new ArrayList<>();
nums.add(2);
nums.add(3.14);
System.out.println(nums);

Number one = nums.get(0);
Number two = nums.get(1);
System.out.println(one);
System.out.println(two);


Here, subtyping is used in two ways for each method call. The first call is permitted because nums has type List, which is a subtype of Collection, and 2 has type Integer (thanks to boxing), which is a subtype of Number. The second call is similarly permitted. In both calls, the E in List is taken to be Number. It may seem reasonable to expect that since Integer is a subtype of Number, it follows that List is a subtype of List. But this is not the case, because the Substitution Principle would rapidly get us into trouble. It is not always safe to assign a value of type List to a variable of type List. Consider the following code fragment:

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);

List<Number> nums = ints; // compile-time error
nums.add(3.14);

This code assigns variable ints to point at a list of integers, and then assigns nums to point at the same list of integers; hence the call in the fifth line adds a double to this list, as shown in the last line. This must not be allowed! The problem is prevented by observing that here the Substitution Principle does not apply: the assignment on the fourth line is not allowed because List<Integer> is not a subtype of List<Number>, and the compiler reports that the fourth line is in error. 


What about the reverse? Can we take List<Number> to be a subtype of List<Integer>? No, that doesn’t work either, as shown by the following code: 

List<Number> nums = new ArrayList<>();
nums.add(2.76);
nums.add(3.14);

List<Integer> ints = nums; // compile-time error


3. Wildcard with extends

Clearly, given a collection of elements of type E, it is OK to add all members of another collection with elements of type E. The quizzical phrase "? extends E" means that it is also OK to add all members of a collection with elements of any type that is a subtype of E. The question mark is called a wildcard, since it stands for some type that is a subtype of E.

List<Number> nums = new ArrayList<>();
List<Integer> ints = Arrays.asList(1, 2);
List<Double> dbls = Arrays.asList(2.78, 3.14);
nums.addAll(ints);
nums.addAll(dbls);
System.out.println(nums);

The first call is permitted because nums has type List<Number>, which is a subtype of Collection, and ints has type List<Integer>, which is a subtype of Collection. The second call is similarly permitted. In both calls, E is taken to be Number. If the method signature for addAll had been written without the wildcard, then the calls to add lists of integers and doubles to a list of numbers would not have been permitted; you would only have been able to add a list that was explicitly declared to be a list of numbers. 



We can also use wildcards when declaring variables. Here is a variant of the example at the end of the preceding section, changed by adding a wildcard to the second line: 

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

Before, the fourth line caused a compile-time error (because List is not a subtype of List), but the fifth line was fine (because a double is a number, so you can add a double to a List). Now, the fourth line is fine (because List is a subtype of List), but the fifth line causes a compile-time error (because you cannot add a double to a List, since it might be a list of some other subtype of number). As before, the last line shows why one of the preceding lines is illegal! 

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.

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3);



5. Wildcards with super

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

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));
}
}

The quizzical phrase ? super T means that the destination list may have elements of any type that is a supertype of T, just as the source list may have elements of any type that is a subtype of T. 

We could also declare the method with several possible signatures. public static void copy(List dst, List src) public static void copy(List dst, List src) public static void copy(List dst, List src) public static void copy(List dst, List src) The first of these is too restrictive, as it only permits calls when the destination and source have exactly the same type. The remaining three are equivalent for calls that use implicit type parameters, but differ for explicit type parameters. For the example calls above, the second signature works only when the type parameter is Object, the third signature works only when the type parameter is Integer, and the last signature works (as we have seen) for all three type parameters—i.e., Object, Number, and Integer. Always use wildcards where you can in a signature, since this permits the widest range of calls.


6. The Get and Put principle

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don’t use a wildcard when you both get and put


Whenever you use an iterator, you get values out of a structure, so use an extends wildcard. Here is a method that takes a collection of numbers, converts each to a double, and sums them up:

public static double sum(Collection<? extends Number> nums) {
double s = 0;
for (Number num : nums) s += num.doubleValue();
return s;
}

Since this uses extends, all of the following calls are legal:

List<Integer> ints = Arrays.asList(1, 2, 3);
System.out.println(sum(ints));
List<Double> doubles = Arrays.asList(2.78, 3.14);
System.out.println(sum(doubles));
List<Number> nums = Arrays.asList(1, 2, 2.78, 3.14);
System.out.println(sum(nums));

The first two calls would not be legal if extends was not used.


Whenever you use the add method, you put values into a structure, so use a super wildcard. Here is a method that takes a collection of numbers and an integer n, and puts the first n integers, starting from zero, into the collection:

public static void count(Collection<? super Integer> ints, int n) {
for (int i = 0; i < n; i++) ints.add(i);
}
List<Integer> ints = new ArrayList<>();
count(ints, 5);
System.out.println(ints);

List<Number> nums = new ArrayList<>();
count(nums, 5);
nums.add(5.0);
System.out.println(nums);

List<Object> objs = new ArrayList<>();
count(objs, 5);
objs.add("five");
System.out.println(objs);

The last two calls would not be legal if super was not used.


Whenever you both put values into and get values out of the same structure, you should not use a wildcard. 

public static double sumCount(Collection<Number> nums, int n) {
count(nums, n);
return sum(nums);
}

The collection is passed to both sum and count, so its element type must both extend Number (as sum requires) and be super to Integer (as count requires). The only two classes that satisfy both of these constraints are Number and Integer, and we have picked the first of these. Here is a sample call: 

List<Number> nums = new ArrayList<>();
double sum = sumCount(nums, 5);
System.out.println(sum);


The Get and Put Principle also works the other way around. If an extends wildcard is present, pretty much all you will be able to do is get but not put values of that type; and if a super wildcard is present, pretty much all you will be able to do is put but not get values of that type. For example, consider the following code fragment, which uses a list declared with an extends wildcard:

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
double dbl = sum(nums); // ok
nums.add(3.14); // compile-time error

The call to sum is fine, because it gets values from the list, but the call to add is not, because it puts a value into the list. This is just as well, since otherwise we could add a double to a list of integers!

The exception proves the rule, and each of these rules has one exception. You cannot put anything into a type declared with an extends wildcard—except for the value null, which belongs to every reference type.



Similarly, you cannot get anything out from a type declared with a super wildcard— except for a value of type Object, which is a supertype of every reference type:

List<Object> objs = Arrays.asList(1, "two");
List<? super Integer> ints = objs;
String str = "";
for (Object obj : ints) str += obj.toString();
System.out.println(str);

You may find it helpful to think of ? extends T as containing every type in an interval bounded by the type of null below and by T above (where the type of null is a subtype of every reference type). Similarly, you may think of ? super T as containing every type in an interval bounded by T below and by Object above.


7. Arrays

 Consider the following code fragment, which allocates an array of integers, assigns it to an array of numbers, and then attempts to assign a double into the array:

Integer[] ints = new Integer[] {1,2,3};
Number[] nums = ints;
nums[2] = 3.14; // array store exception


In contrast, the subtyping relation for generics is invariant, meaning that type List is not considered to be a subtype of List, except in the trivial case where S and T are identical. Here is a code fragment analogous to the preceding one, with lists replacing arrays:

List<Integer> ints = Arrays.asList(1,2,3);
List<Number> nums = ints; // compile-time error
nums.set(2, 3.14);
System.out.println(nums);


Wildcards reintroduce covariant subtyping for generics, in that type List is considered to be a subtype of List when S is a subtype of T. Here is a third variant of the fragment: 

List<Integer> ints = Arrays.asList(1,2,3);
List<? extends Number> nums = ints;
nums.set(2, 3.14); // compile-time error


As with arrays, the third line is in error, but, in contrast to arrays, the problem is detected at compile time, not run time. The assignment violates the Get and Put Principle, because you cannot put a value into a type declared with an extends wildcard.


Wildcards also introduce contravariant subtyping for generics, in that type List is considered to be a subtype of List when S is a supertype of T (as opposed to a subtype). Arrays do not support contravariant subtyping. For instance, recall that the method count accepted a parameter of type Collection and filled it with integers. There is no equivalent way to do this with an array, since Java does not permit you to write (? super Integer)[].


8. Wildcards vs Type Parameters

Wildcards Here are the types that the methods have in Java with generics:

 interface Collection

 ... public boolean contains(Object o);

 public boolean containsAll(Collection c); ... 

The first method does not use generics at all! The second method is our first sight of an important abbreviation. The type Collection stands for: Collection Extending Object is one of the most common uses of wildcards, so it makes sense to provide a short form for writing it.


9. Wildcard capture

Consider the method reverse in the convenience class java.util.Collections, which accepts a list of any type and reverses it. It can be given either of the following two signatures, which are equivalent:

 public static void reverse(List list);

 public static void<T>  reverse(List<T> list); 

The wildcard signature is slightly shorter and clearer, and is the one used in the library. If you use the second signature, it is easy to implement the method:

public static <T> void reverse(List<T> list) {
List<T> tmp = new ArrayList<>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1));
}
}

If you try to use the first signature with a similar method body, it won’t work: 

public static void reverse2(List<?> list) {
List<Object> tmp = new ArrayList<>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1));
}
}

Now it is not legal to write from the copy back into the original, because we are trying to write from a list of objects into a list of unknown type. Replacing List with List won’t fix the problem, because now we have two lists with (possibly different) unknown element types.


Instead, you can implement the method with the first signature by implementing a private method with the second signature, and calling the second from the first:

public static <T> void reverse(List<T> list) {
List<T> tmp = new ArrayList<>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1));
}
System.out.println(list);
}

public static void reverse2(List<?> list) {
reverse(list);
}



                                                                Comparison and Bounds

10. Comparator

Sometimes we want to compare objects that do not implement the Comparable interface, or to compare objects using a different ordering from the one specified by that interface. The ordering provided by the Comparable interface is called the natural ordering, so the Comparator interface provides, so to speak, an unnatural ordering.


Permitting comparison of apples with oranges:

package org.example.fruityExample;

import java.util.Objects;

public class Fruit implements Comparable<Fruit> {
private String name;
private Integer size;

public Fruit() {
}

public Fruit(Integer size) {
this.size = size;
}

public Fruit(String name, Integer size) {
this.name = name;
this.size = size;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getSize() {
return size;
}

public void setSize(Integer size) {
this.size = size;
}


@Override
public boolean equals(Object o) {
if (o instanceof Fruit) {
Fruit that = (Fruit) o;
return this.name.equals(that.name) && this.size == that.size;
} else return false;
}

@Override
public int hashCode() {
return name.hashCode() * 29 + size;
}

@Override
public int compareTo(Fruit that) {
return this.size.compareTo(that.size);
}

@Override
public String toString() {
return "Fruit{" +
"name='" + name + '\'' +
", size=" + size +
'}';
}
}
package org.example.fruityExample;

public class Apple extends Fruit{
public Apple() {
}

public Apple(Integer size) {
super(size);
}

public Apple(String name, Integer size) {
super(name, size);
}
}
package org.example.fruityExample;

public class Orange extends Fruit{
public Orange() {
}

public Orange(Integer size) {
super(size);
}

public Orange(String name, Integer size) {
super(name, size);
}
}
package org.example.fruityExample;

import java.lang.reflect.Array;
import java.util.*;

public class Main {
public static void main(String[] args) {
Apple a1 = new Apple(1);
Apple a2 = new Apple(2);
Orange o3 = new Orange(3);
Orange o4 = new Orange(4);

List<Apple> apples = Arrays.asList(a1, a2);
System.out.println(Collections.max(apples));

List<Orange> oranges = Arrays.asList(o3, o4);
System.out.println(Collections.max(oranges));

List<Fruit> mixed = Arrays.asList(a1, o3);
System.out.println(Collections.max(mixed));
}

public static <T extends Comparable<T>> T max(Collection<T> coll) {
Iterator<T> it = coll.iterator();
T candidate = it.next();
while (it.hasNext()) {
T elt = it.next();
if (candidate.compareTo(elt) < 0) candidate = elt;
}
return candidate;
}
}


Prohibiting comparison of apples with oranges:

package org.example.fruityExample2;

public class Fruit {
private String name;
private Integer size;

public Fruit(String name, Integer size) {
this.name = name;
this.size = size;
}

public boolean equals(Object o) {
if (o instanceof Fruit) {
Fruit that = (Fruit) o;
return this.name.equals(that.name) && this.size == that.size;
} else return false;
}

public int hashCode() {
return name.hashCode() * 29 + size;
}

protected int compareTo(Fruit that) {
return this.size.compareTo(that.size);
}
}
package org.example.fruityExample2;


public class Apple extends Fruit implements Comparable<Apple> {

public Apple(Integer size) {
super("Apple", size);
}

@Override
public int compareTo(Apple a) {
return super.compareTo(a);
}
}
package org.example.fruityExample2;


public class Orange extends Fruit implements Comparable<Orange> {

public Orange(int size) {
super("Orange", size);
}

@Override
public int compareTo(Orange o) {
return super.compareTo(o);
}
}
package org.example.fruityExample2;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Main {
public static void main(String[] args) {
Apple a1 = new Apple(1);
Apple a2 = new Apple(2);
Orange o3 = new Orange(3);
Orange o4 = new Orange(4);

List<Apple> apples = Arrays.asList(a1, a2);
System.out.println(Collections.max(apples));

List<Orange> oranges = Arrays.asList(o3, o4);
System.out.println(Collections.max(oranges));

List<Fruit> mixed = Arrays.asList(a1, o3);
System.out.println(Collections.max(mixed));
}
}



11. Bridges

As we mentioned earlier, generics are implemented by erasure: when you write code with generics, it compiles in almost exactly the same way as the code you would have written without generics. In the case of a parameterized interface such as Comparable<T>, this may cause additional methods to be inserted by the compiler; these additional methods are called bridges.

The bridge method is generated automatically by the compiler. Indeed, the compiled version of the code for both examples is essentially identical. Example 3-6. Legacy code for comparable integers: 

interface Comparable { public int compareTo(Object o); } 

class Integer implements Comparable { 

 private final int value; public Integer(int value) 

{ this.value = value; }

 public int compareTo(Integer i) 

{ return (value < i.value) ? -1 : (value == i.value) ? 0 : 1; }

 public int compareTo(Object o) 

{ return compareTo((Integer)o); }

 }

 Example 3-7. Generic code for comparable integers:

 interface Comparable { public int compareTo(T o); } 

class Integer implements Comparable 

{ private final int value; public Integer(int value) 

{ this.value = value; } public int compareTo(Integer i) 

{ return (value < i.value) ? -1 : (value == i.value) ? 0 : 1; } 


You can see the bridge if you apply reflection. Here is code that finds all methods with the name compareTo in the class Integer, using toGenericString to print the generic signature of a method


for (Method m : Integer.class.getMethods()) if (m.getName().equals("compareTo")) System.out.println(m.toGenericString()); 


12. Constructors

package org.example.test9;

public class Pair<T, U> {
private final T first;
private final U second;

public Pair(T first, U second) {
this.first = first;
this.second = second;
}

public T getFirst() {
return first;
}

public U getSecond() {
return second;
}
}

he type parameters T and U are declared at the beginning of the class, not in the constructor. However, actual type parameters are passed to the constructor whenever it is invoked:

package org.example.test9;

public class Main {
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("one", 2);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());
}
}


13. With and without generics

-- Legacy library with legacy client:

package org.example.test10;

public interface Stack {
public boolean empty();
public void push(Object elt);
public Object pop();
}
package org.example.test10;

import java.util.ArrayList;
import java.util.List;

public class ArrayStack implements Stack {
private List list;

public ArrayStack() {
list = new ArrayList();
}

@Override
public boolean empty() {
return list.size() == 0;
}

@Override
public void push(Object elt) {
list.add(elt);
}

@Override
public Object pop() {
Object elt = list.remove(list.size() - 1);
return elt;
}

public String toString() {
return "stack" + list.toString();
}
}
package org.example.test10;

public class Stacks {
public static Stack reverse(Stack in) {
Stack out = new ArrayStack();
while (!in.empty()) {
Object elt = in.pop();
out.push(elt);
}
return out;
}
}
package org.example.test10;

public class Main {
public static void main(String[] args) {
Stack stack = new ArrayStack();
for (int i = 0; i < 4; i++) stack.push(new Integer(i));
System.out.println(stack);

Stack reverse = Stacks.reverse(stack);
System.out.println(reverse);
}
}


-- Generic library with generic client:

package org.example.test11;

public interface Stack<E> {
public boolean empty();
public void push(E elt);
public E pop();
}
package org.example.test11;

import java.util.ArrayList;
import java.util.List;

public class ArrayStack<E> implements Stack<E> {
private List<E> list;

public ArrayStack() {
list = new ArrayList<>();
}

@Override
public boolean empty() {
return list.size() == 0;
}

@Override
public void push(E elt) {
list.add(elt);
}

@Override
public E pop() {
E elt = list.remove(list.size() - 1);
return elt;
}

public String toString() {
return "stack" + list.toString();
}
}
package org.example.test11;

public class Stacks {
public static <T> Stack<T> reverse(Stack<T> in) {
Stack<T> out = new ArrayStack<>();
while (!in.empty()) {
T elt = in.pop();
out.push(elt);
}
return out;
}
}
package org.example.test11;

public class Main {
public static void main(String[] args) {
Stack<Integer> stack = new ArrayStack<Integer>();
for (int i = 0; i<4; i++) stack.push(i);
System.out.println(stack);

int top = stack.pop();
System.out.println(top);

Stack<Integer> reverse = Stacks.reverse(stack);
System.out.println(reverse);
}
}




-- Converting collection to an array:

package org.example.test14;

import java.util.Collection;

public class Right {

public static <T> T[] toArray(Collection<T> c, T[] a) {
if (a.length < c.size())
a = (T[]) java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), c.size());
int i = 0;
for (T x : c) a[i++] = x;
if (i < a.length) a[i] = null;
return a;
}
}
package org.example.test14;

import java.util.Arrays;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<String> strings = Arrays.asList("one", "two");
String[] a = Right.toArray(strings, new String[0]);
System.out.println(Arrays.toString(a));

String[] b = new String[] {"x", "x", "x", "x", "x"};
Right.toArray(strings, b);
System.out.println(Arrays.toString(b));
}
}


-- Converting collection to an array:


public static <T> T[] toArray(Collection<T> c, Class<T> k) {
T[] a = (T[]) java.lang.reflect.Array.newInstance(k, c.size());
int i = 0;
for (T x : c) a[i++] = x;
return a;
}
public static void main(String[] args) {
List<String> strings = Arrays.asList("one", "two");
String[] a = Right.toArray(strings, String.class);
System.out.println(Arrays.asList(a));
}


14. The principle of indecent exposure.

This assigns an array of integers to an array of numbers, and then attempts to store a double into the array of numbers.

package org.example.test14;

public class Main {
public static void main(String[] args) {
Integer[] ints = new Integer[]{1};
Number[] nums = ints;
nums[0] = 1.1; // array store exception
int n = ints[0];
}
}


Here is a similar example, where arrays of numbers are replaced by arrays of lists of numbers:

package org.example.test14;

import java.util.Arrays;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<Integer>[] intLists = (List<Integer>[]) new List[]{Arrays.asList(1)}; // unchecked cast
List<? extends Number>[] numLists = intLists;
numLists[0] = Arrays.asList(1.1);
int n = intLists[0].get(0); // class cast exception
}
}


-- Avoid arrays of nonreifiable type

package org.example.test14;

import java.util.Arrays;
import java.util.List;

public class DeceptiveLibrary {

public static List<Integer>[] intLists(int size) {
List<Integer>[] intLists = (List<Integer>[]) new List[size]; // unchecked cast
for (int i = 0; i < size; i++)
intLists[i] = Arrays.asList(i + 1);
return intLists;
}
}
package org.example.test14;

import java.util.Arrays;
import java.util.List;

public class InnocentClient {
public static void main(String[] args) {
List<Integer>[] intLists = DeceptiveLibrary.intLists(1);
List<? extends Number>[] numLists = intLists;
numLists[0] = Arrays.asList(1.1);
int i = intLists[0].get(0); // class cast exception
}
}


-- How to define ArrayList

package org.example.test15;

import java.util.AbstractList;
import java.util.Collection;
import java.util.RandomAccess;

public class ArrayList<E> extends AbstractList<E> implements RandomAccess {
private E[] arr;
private int size = 0;

public ArrayList(int cap) {
if (cap < 0)
throw new IllegalArgumentException("Illegal capacity: " + cap);
arr = (E[]) new Object[cap]; // unchecked cast
}

public ArrayList() {
this(10);
}

public ArrayList(Collection<? extends E> c) {
this(c.size());
addAll(c);
}

public void ensureCapacity(int mincap) {
int oldcap = arr.length;
if (mincap > oldcap) {
int newcap = Math.max(mincap, (oldcap * 3) / 2 + 1);
E[] oldarr = arr;
arr = (E[]) new Object[newcap]; // unchecked cast
System.arraycopy(oldarr, 0, arr, 0, size);
}
}

public int size() {
return size;
}

private void checkBounds(int i, int size) {
if (i < 0 || i >= size)
throw new IndexOutOfBoundsException("Index: " + i + ", Size: " + size);
}

public E get(int i) {
checkBounds(i, size);
return arr[i];
}

public E set(int i, E elt) {
checkBounds(i, size);
E old = arr[i];
arr[i] = elt;
return old;
}

public void add(int i, E elt) {
checkBounds(i, size + 1);
ensureCapacity(size + 1);
System.arraycopy(arr, i, arr, i + 1, size - i);
arr[i] = elt;
size++;
}

public E remove(int i) {
checkBounds(i, size);
E old = arr[i];
arr[i] = null;
size--;
System.arraycopy(arr, i + 1, arr, i, size - i);
return old;
}

public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[]) java.lang.reflect.Array. // unchecked cast
newInstance(a.getClass().getComponentType(), size);
System.arraycopy(arr, 0, a, 0, size);
if (size < a.length) a[size] = null;
return a;
}

public Object[] toArray() {
return toArray(new Object[0]);
}
}


-----------------------------------------------------------------------------------------------------------------------------

                                                                    Design Patterns

1. Visitor

-- A simple tree and client

package org.example.visitor;

public abstract class Tree<E> {
abstract public String toString();

abstract public Double sum();

public static <E> Tree<E> leaf(final E e) {
return new Tree<E>() {
@Override
public String toString() {
return e.toString();
}

@Override
public Double sum() {
return ((Number) e).doubleValue();
}
};
}

public static <E> Tree<E> branch(final Tree<E> l, final Tree<E> r) {
return new Tree<E>() {
@Override
public String toString() {
return "(" + l.toString() + "^" + r.toString() + ")";
}

@Override
public Double sum() {
return l.sum() + r.sum();
}
};
}
}
package org.example.visitor;

public class Main {
public static void main(String[] args) {
Tree<Integer> t = Tree.branch(Tree.branch(Tree.leaf(1), Tree.leaf(2)), Tree.leaf(3));
System.out.println(t);
System.out.println(t.sum());
}
}

The Visitor pattern makes it possible to provide new operations without modifying the classes that define the data structure. In this pattern, the abstract class that represents the structure declares an abstract visit method, which takes a visitor as an argument. The visitor implements an interface that specifies one method for each case in the specification of the structure. Each subclass implements the visit method by calling the method of the visitor for the corresponding case.


-- A tree with visitors


















-----------------------------------------------------------------------------------------------------------------------------










-----------------------------------------------------------------------------------------------------------------------------

-- Unbounded wildcard example

package org.example.test18;

public interface Shape {
public void draw();
}
package org.example.test18;

public class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("Drawing rectangle on the canvas...");
}
}
package org.example.test18;

public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing circle on the canvas...");
}
}
package org.example.test18;

import java.util.ArrayList;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<Rectangle> rectangles = new ArrayList<>();
drawAllShapes(rectangles);
}

public static void drawAllShapes(List<Shape> shapes) {
for (Shape shape : shapes)
shape.draw();
}
}

Yuxarida compile time xetanin sebebi odurki, Java List<Shape> List<Rectangle> supertype deyil.


Indi ise metodu deyiwek:

package org.example.test18;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<Shape> shapes = Arrays.asList(new Rectangle(), new Circle());
drawAllShapes(shapes);

List<Rectangle> rectangles = Arrays.asList(new Rectangle());
drawAllShapes(rectangles);
}

public static <T extends Shape> void drawAllShapes(List<T> shapes) {
for (T shape : shapes)
shape.draw();
}
}


-- Upper Bounded Wildcards




























Комментарии

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

IoC:ApplicationContext, BeanFactory. Bean

Lesson1: JDK, JVM, JRE

Lesson_2: Operations in Java