SE_21_Lesson_16:Generics
-> Generic olmadan adi Box klasi uzerinde numune
package org.example;
public class Box {
private Object object;
public Box(Object object) {
this.object = object;
}
public Object giveMeAnObject() {
System.out.println("Preparing object...");
return this.object;
}
}
package org.example;
public class Person {
private String name;
private String surname;
public Person() {
}
public Person(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 String.format("Person{name='%s', surname='%s'}", name, surname);
}
}
package org.example;
public class Main {
public static void main(String[] args) {
Box box = new Box(new Person("Ali", "Aliyev"));
unpackBox(box);
}
public static void unpackBox(Box box) {
Person ali = (Person) box.giveMeAnObject();
System.out.println(ali);
}
}
Burada bize her wey normal gorsenir. Lakin Box constructoruna mes: String gonderdikde xeta baw verir.
package org.example;
public class Main {
public static void main(String[] args) {
Box box = new Box(new String());
unpackBox(box);
}
public static void unpackBox(Box box) {
Person ali = (Person) box.giveMeAnObject();
System.out.println(ali);
}
}
Problem 1:
package org.oracle;
public class MyClassIntegers {
Integer i;
public MyClassIntegers(Integer i) {
this.i = i;
}
Integer returnStuff() {
return i;
}
}
package org.oracle;
public class MyClassDouble {
Double d;
public MyClassDouble(Double d) {
this.d = d;
}
Double returnStuff() {
return d;
}
}
package org.oracle;
public class MyClassObject {
Object obj;
public MyClassObject(Object obj) {
this.obj = obj;
}
Object returnStuff() {
return obj;
}
}
Solution: We can create one class and it can take in different types of variables
package org.oracle;
public class MyClass<T> {
T ob;
public MyClass(T ob) {
this.ob = ob;
}
void showType() {
System.out.println(ob.getClass().getName());
}
}
-> Generic klass type safety yaradir. Box klasini generalize edek
Artiq generalize olanda xetani derhal gore bilirik
package org.example;
public class Box<T> {
private T object;
public Box(T object) {
this.object = object;
}
public T giveMeAnObject() {
System.out.println("Preparing object...");
return this.object;
}
}
package org.example;
public class Main {
public static void main(String[] args) {
Box<Person> box = new Box<Person>(new String());
unpackBox(box);
}
public static void unpackBox(Box box) {
Person ali = (Person) box.giveMeAnObject();
System.out.println(ali);
}
}
Burada metodun icine diqqet etsek casting-e ehtiyac qalmadigini gosterir:
package org.example;
public class Main {
public static void main(String[] args) {
Box<Person> box = new Box<Person>(new Person("Ali", "Aliyev"));
unpackBox(box);
}
public static void unpackBox(Box<Person> box) {
Person ali = (Person) box.giveMeAnObject();
System.out.println(ali);
}
}
*** Biz fikirlewe bilerk ki, Box klasinin icinde Person obyekti yaradaq ve hec bir problem yawamayaq, o zaman bizim yaratdigimiz Box klasi yalniz Person-a xidmet edecek. O zaman fikirlewe bilerk ki, ele evvelki variantinda yeni icinde Object olanda daha yaxwidir, bu zaman ise biz type safety-ni itiriri ve casting problemi yawaya bilerik.
-> Raw type anlayishi
package org.example;
public class Main {
public static void main(String[] args) {
Box box = new Box(new Person("Ali", "Aliyev"));
Object object = box.giveMeAnObject();
Box<Person> box1 = new Box<>(new Person("Ali", "Aliyev"));
Person person = box1.giveMeAnObject();
unpackBox(box);
}
public static void unpackBox(Box<Person> box) {
Person ali = (Person) box.giveMeAnObject();
System.out.println(ali);
}
}
-> For backward compatibility, assigning a parameterized type to its raw type is allowed:
package org.oracle;
public class Box <T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
package org.oracle;
public class Main {
public static void main(String[] args) {
Box<String> box = new Box<>();
Box box1 = box;
}
}
But if you assign a raw type to a parameterized type, you get a warning:
package org.oracle;
public class Main {
public static void main(String[] args) {
Box rawBox = new Box();
Box<Integer> intBox = rawBox; // warning: unchecked conversion
}
}
You also get a warning if you use a raw type to invoke generic methods in the corresponding generic type:
package org.oracle;
import org.example.Person;
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(new Person()); // warning: unchecked invocation to set(T)
System.out.println(rawBox.get());
Box rb = new Box<String>();
rb.set(new Person("Ali", "Aliyev"));
System.out.println(rb.get());
}
}
The warning shows that raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Therefore, you should avoid raw types.
-> Constructorda diamond operatoru ishletmesek Java bunu raw type kimi anlayir ve compile type error vermir, lakin runtime error vere biler
package org.example;
public class Main {
public static void main(String[] args) {
Box<Person> box = new Box(new String()); // raw type kimi qebul edir
Person person = box.giveMeAnObject();
Box<Person> box1 = new Box<>(new String());
Person person1 = box1.giveMeAnObject();
}
public static void unpackBox(Box<Person> box) {
Person ali = (Person) box.giveMeAnObject();
System.out.println(ali);
}
}
-> Multiple type parameter
package org.oracle;
public interface Pair <K, V>{
public K getKey();
public V getValue();
}
package org.oracle;
public class OrderPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderPair(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
}
package org.oracle;
public class Main {
public static void main(String[] args) {
Pair<String, Integer> p1 = new OrderPair<>("One", 1);
Pair<String, String> p2 = new OrderPair<>("Two", "Iki");
System.out.println(p1.getKey());
System.out.println(p1.getValue());
}
}
-> Bounded type in class:
Problem2:
package org.oracle;
public class NumberFns<T> {
T ob;
NumberFns(T ob) {
this.ob = ob;
}
double square() {
return ob * ob;
}
}
Solution: We can use bounded type
package org.oracle;
public class NumberFns<T extends Number> { // upper bound
T ob;
NumberFns(T ob) {
this.ob = ob;
}
double square() {
return ob.intValue() * ob.doubleValue();
}
}
package org.oracle;
public class Main {
public static void main(String[] args) {
NumberFns<Integer> iob = new NumberFns<>(4);
System.out.println(iob.square());
}
}
-> Generic methods:
package org.oracle;
public class Pair <K, V>{
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
package org.oracle;
public class Util {
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue());
}
}
-> Bounded type parameters:
package org.oracle;
public class Box <T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public <U extends Number> void inspect(U u) {
System.out.println("T: " + t.getClass().getName());
System.out.println("U: " + u.getClass().getName());
}
}
package org.oracle;
public class Main {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<>();
integerBox.setT(new Integer(10));
integerBox.inspect("abc"); // error: this is String
}
}
-> Generic methods and bounded type parameters
Bounded type parameters are key to the implementation of generic algorithms. Consider the following
method that counts the number of elements in an array T[] that are greater than a specified element.
public static <T> int countGreaterThan(T[] array, T element) {
int count = 0;
for (T e : array)
if (e > element) // compiler error
count++;
return count;
}
The implementation of the method is straightforward, but it does not compile because the greater than operator(>) applies only to primitive types such as short, int, double, long, float, byte and char. You cannot use the > operator to compare objects. To fix the problem, use a type parameter bounded by the Comparable<T> interface:
public static <T extends Comparable<T>> int countGreaterThan(T[] array, T element) {
int count = 0;
for (T e : array)
if (e.compareTo(element) > 0)
count++;
return count;
}
-> Generics, Inheritance, and Subtypes
As you already know, it is possible to assign an object of one type to an object of another type provided that the types are compatible. For example, you can assign an Integer to an Object, since Object is one of Integer's supertypes:
Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger;
But Integer is also a kind of Number, so the following code is valid as well:
public void someMethod(Number n) { /* ... */ }package org.oracle;
public class Main {
public static void main(String[] args) {
foo(new Integer(10));
foo(new Double(5.5));
}
public static void foo(Number n) {
}
}
The same is also true with generics. You can perform a generic type invocation, passing Number as its type argument, and any subsequent invocation of add will be allowed if the argument is compatible with Number:
package org.oracle;
public class Main {
public static void main(String[] args) {
Box<Number> box = new Box<>();
box.setT(new Integer(10));
box.setT(new Double(5.5));
}
}
Now consider the following method:
public static void foo(Box<Number> box) {
/*
some code
*/
}
What type of argument does it accept? By looking at its signature, you can see that it accepts a single argument whose type is Box<Number>. But what does that mean? Are you allowed to pass in Box<Integer> or Box<Double>, no of course. Because, Box<Integer> and Box<Double> are not subtypes of Box<Number>.
-> Upper bounded wildcards
To write the method that works on list of Number and the subtypes of Number, such as Integer, Double and Float, you would specify List<? extends Number> . The term List<Number> is more restrictive then List<? extends Number> because the former matches a list of type Number only, whereas the latter matches a list of type Number or any of its subclass.
package org.oracle;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3);
System.out.println(sumOfList(integers));
List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
System.out.println(sumOfList(doubles));
}
public static double sumOfList(List<? extends Number> list) {
double s = 0;
for (Number n : list)
s += n.doubleValue();
return s;
}
}
-> Unbounded wildcards
package org.oracle;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
printList(list);
}
public static void printList(List<Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
}
The goal of printList is to print a list of any type, but it fails to achieve that goal - it prints only a list of Object instances; it cannot print List<Integer>, List<String>, List<Double>, and so on, because they are not subtypes of List<Object>. To write a generic printList method, use List<?>
package org.oracle;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
printList(list);
}
public static void printList(List<?> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
}
Because for any concrete type A, List<A> is a subtype of List<?>, you can use printList to print a list of any type.
*** It is important to note that List<Object> and List<?> are not the same. You can insert an Object, or any subtype of Object, into a List<Object>. But you can insert null into a List<?>.
For example: List<Object> and List<? extends Object>(is equivalent to List<?>) are totally different things. Carefully took at below code snippet:
package lesson22;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
printList(list);
}
public static void printList(List<? extends Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
}
package lesson22;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
printList(list);
}
public static void printList(List<Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
}
The former example throw compile time exception, because List<Integer> does not extend List<Object>. But List<? extends Object> or List<?> means that we can send as an argument anything including List<Integer>.
Problem3:
package org.oracle;
public class NumberFns<T extends Number> { // upper bound
T num;
public NumberFns(T num) {
this.num = num;
}
boolean absEqual(NumberFns<T> ob) {
if (Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue()))
return true;
return false;
}
}
package org.oracle;
public class Main {
public static void main(String[] args) {
NumberFns<Integer> iob = new NumberFns<>(4);
NumberFns<Double> dob = new NumberFns<>(-4.0);
iob.absEqual(dob);
}
}
Solution:
package org.oracle;
public class NumberFns<T extends Number> { // upper bound
T num;
NumberFns(T num) {
this.num = num;
}
boolean absEqual(NumberFns<?> ob) { // ? unbounded type
if (Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue()))
return true;
return false;
}
}
-> Lower bounded wildcard
A lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type.
Say you want to write a method that puts Integer objects into a list. To maximize flexibility, you would like the method to work on List<Integer>, List<Number>, and List<Object> - anything than can hold Integer values.
To write the method that works on lists of Integer, such as Integer, Number, and Object, you would specify List<? super Integer>. The term List<Integer> is more restrictive than List<? super Integer> because the former matches a list of type Integer only, whereas the latter matches a list of any type that is a supertype of Integer.
package org.oracle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
list.add(new Object());
addNumber(list);
System.out.println(list);
list.add(new Object());
System.out.println(list);
list.add(2.2);
System.out.println(list);
}
public static void addNumber(List<? super Integer> list) {
for (int i = 1; i <= 10; i++)
list.add(i);
}
}
-> Erasure of generic types
During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded.
Consider the following generic class that represents a node in a singly linked list:
package org.example;
public class Node<T> {
public T data;
public Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
}
Because the type parameter T is unbounded, the Java compiler replaces it with Object:
package org.example;
public class Node {
public Object data;
public Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
}
In the following example, the generic Node class uses a bounded type parameter:
package org.example;
public class Node<T extends Comparable<T>> {
public T data;
public Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
}
The Java compiler replaces the bounded type parameter T with the first bounded class, Comparable:
package org.example;
public class Node {
public Comparable data;
public Node next;
public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
}
-> Erasure of Generic methods
The Java compiler also erases type parameters in generic method arguments.
public static <T> int count(T[] arr, T elem) {
int cnt = 0;
for (T e : arr)
if (e.equals(elem))
++cnt;
return cnt;
}
Because T is unbounded, the Java compiler replaces it with Object:
public static int count(Object[] arr, Object elem) {
int cnt = 0;
for (Object e : arr)
if (e.equals(elem))
++cnt;
return cnt;
}
Suppose the following classes are defined:
class Shape { /* ... */ } class Circle extends Shape { /* ... */ } class Rectangle extends Shape { /* ... */ }
You can write a generic method to draw different shapes:
public static <T extends Shape> void draw(T shape) { /* ... */ }
The Java compiler replaces T with Shape:
public static void draw(Shape shape) { /* ... */ }
-----------------------------------------------------------------------------------------------------------------------------
Question:
package org.cayHortsman;
public interface Runnable extends Comparable<Runnable>{
}
package org.cayHortsman;
public class Runner implements Runnable{
@Override
public int compareTo(Runnable o) {
return 0;
}
}
package org.cayHortsman;
public class Pair <T extends Comparable<T>>{
private T first;
private T second;
public Pair() {
}
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
}
package org.cayHortsman;
public class Main {
public static void main(String[] args) {
Pair<Runner>
}
}
Pervin arashdir niye bele olur?
===========================================================================
Arashdir!!!
package lesson25;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// List<D> dList = new ArrayList<>();
// dList.add(new D());
// dList.add(new D());
// dList.add(new D());
// List<? extends Object> objects = new ArrayList<>();
// objects = dList;
// List<Object> objects1 = new ArrayList<>();
// objects1.add(new D());
//
// List<? super Number> list = new ArrayList<>();
// list = objects1;
// list.add(5);
// list.add(5.5);
// System.out.println(list);
List<? extends Object> objects = new ArrayList<>();
List<? super Number> list = new ArrayList<>();
list = objects;
}
}
Комментарии
Отправить комментарий