Generics. (Lecture 7.3)

Содержание

Слайд 2

What if you could say that your code works with "some

What if you could say that your code works with "some

unspecified type," rather than a specific interface or class?
Слайд 3

Generics implement the concept of parameterized types, which allow multiple types.

Generics implement the concept of parameterized types, which allow multiple types.


Слайд 4

A class that holds a single object class Automobile {} public

A class that holds a single object
class Automobile {}
public class Holder1

{
  private Automobile a;
  public Holder1(Automobile a) {
  this.a = a;
  }
  Automobile get() { return a; }
}
Слайд 5

A class that holds an Object public class Holder2 { private

A class that holds an Object
public class Holder2 { private Object

a; public Holder2(Object a) { this.a = a; } public void set(Object a) { this.a = a; } public Object get() { return a; } public static void main(String[] args) { Holder2 h2 = new Holder2(new Automobile()); Automobile a = (Automobile) h2.get(); h2.set("Not an Automobile"); String s = (String) h2.get(); h2.set(1); // Autoboxes to Integer Integer x = (Integer) h2.get(); } }
Слайд 6

Simple generic class public class Holder3 { private T a; public

Simple generic class
public class Holder3
  private T a;   public Holder3(T

a) { this.a = a; } public void set(T a) { this.a = a; } public T get() { return a; } public static void main(String[] args) { Holder3 h3 =         new Holder3(new Automobile()); Automobile a = h3.get(); // No cast needed // h3.set("Not an Automobile"); // Error // h3.set(1); // Error } }
Слайд 7

Generic Types and Methods There can be: Generic classes Generic interfaces

Generic Types and Methods

There can be:
Generic classes
Generic interfaces
Generic methods
Bounded generic types
Generic

wildcards
Слайд 8

The core idea of Java generics: You tell it what type

The core idea of Java generics: You tell it what type

you want to use, and it takes care of the details.
Слайд 9

Type erasure Java generics are implemented using type erasure. This means

Type erasure

Java generics are implemented using type erasure. This means that

any specific type information is erased when you compile your code.
Слайд 10

How type erasure works? public class Holder3 { private T a;

How type erasure works?

public class Holder3 { private T a; public

Holder3(T a) { this.a = a; } public void set(T a) { this.a = a; } public T get() { return a; } }

Holder3 h3 =
    new Holder3(1L);
Long n = h3.get();

public class Holder3 { private Object a; public Holder3(Object a) { this.a = a; } public void set(Object a) { this.a = a; } public Object get() { return a; } }

Holder3 h3 = new Holder3(1L);
Long n = (Long) h3.get();

Слайд 11

Compensating for erasure public class Erased { private final int SIZE

Compensating for erasure public class Erased {   private final int SIZE =

100;   public static void f(Object arg) {     if(arg instanceof T) {} // Error     T var = new T(); // Error     T[] array = new T[SIZE]; // Error     T[] array = (T[])new Object[SIZE]; // Unchecked warning   } }
Слайд 12

Generic class with two types public class TwoTuple { public final

Generic class with two types public class TwoTuple { public final

A first; public final B second; public TwoTuple(A a, B b) { first = a; second = b; } public String toString() { return "(" + first + ", " + second + ")"; } } 
Слайд 13

You cannot use primitives as type parameters! TwoTuple ttsi = new

You cannot use primitives as type parameters! TwoTuple ttsi = new

TwoTuple("hi", 47); but not TwoTuple ttdi = new TwoTuple(47.0, 47);
Слайд 14

Inheritance with generic classes public class ThreeTuple extends TwoTuple { public

Inheritance with generic classes public class ThreeTuple extends TwoTuple

{ public final C third; public ThreeTuple(A a, B b, C c) { super(a, b); third = c; } public String toString() { return "(" + first + ", " + second + ", " +         third +")"; } }
Слайд 15

Generic interface public interface Generator { T next(); } public class

Generic interface
public interface Generator { T next(); } public class Fibonacci implements

Generator {   private int count = 0;   public Integer next() { return fib(count++); }
...
  public static void main(String[] args) {   Fibonacci gen = new Fibonacci();   for(int i = 0; i < 18; i++)   System.out.print(gen.next() + " ");   } }
Слайд 16

Generic Methods public class GenericMethods { public void f(T x) {

Generic Methods public class GenericMethods {   public void f(T x) {    

System.out.println(x.getClass().getName());   }   public static void main(String[] args) {     GenericMethods gm = new GenericMethods();     gm.f("");     gm.f(1);     gm.f(1.0);     gm.f('c');     gm.f(new String[] {“H”, “W”});   } }
Слайд 17

The Syntax for Invoking a Generic Method Generics have an optional

The Syntax for Invoking a Generic Method
Generics have an optional syntax

for specifying the type for a generic method. You can place the data type of the generic in angle brackets, < > , after the dot operator and before the method call.
    gm.f(“a string object”);     gm.f(1);     gm.f(1.0);     gm.f('c');
    gm.f(new String[] {“H”, “W”});
The syntax makes the code more readable and also gives you control over the generic type in situations where the type might not be obvious.
Слайд 18

Leveraging type argument inference public class Tuple { public static TwoTuple

Leveraging type argument inference public class Tuple {   public static

TwoTuple tuple(A a, B b) {     return new TwoTuple(a, b);   }   public static ThreeTuple tuple(A a,B b,C c) {     return new ThreeTuple(a, b, c);   } } ... TwoTuple ttsi = Tuple.tuple(“hi”, 47); ThreeTuple ttsid=Tuple.tuple(“hi”,47,47.0);
Слайд 19

Anonymous inner classes public interface Generator { T next(); } class

Anonymous inner classes public interface Generator { T next(); } class Customer

{ private Customer() {} public static Generator generator() { return new Generator() { public Customer next() { return new Customer(); } }; } } ... Customer customer1 = Customer.generator().next(); Customer customer2 = Customer.generator().next();
Слайд 20

Bounded Generic Types Because erasure removes type information, the only methods

Bounded Generic Types
Because erasure removes type information, the only methods you

can call for an unbounded generic parameter are those available for Object. Bounds allow you to place constraints on the parameter types. Important effect is that you can call methods that are in your bound types.
interface HasColor {
Color getColor();
}
class Colored {   T item;
  public Colored(T item) { this.item = item; }
  public T getItem() { return item; }
  // The bound allows you to call a method:   public Color color() { return item.getColor(); } }
Слайд 21

Compound bounds class Dimension { public int x, y, z; }

Compound bounds
class Dimension { public int x, y, z; } 
// Multiple

bounds: class ColoredDimension { T item; ColoredDimension(T item) { this.item = item; } T getItem() { return item; } Color color() { return item.getColor(); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } }
Слайд 22

Bounds and Inheritance class HoldItem { T item; HoldItem(T item) {

Bounds and Inheritance
class HoldItem {   T item;   HoldItem(T item) { this.item

= item; }   T getItem() { return item; } }
class Colored2 extends HoldItem { // some code here...
Color color() { return item.getColor(); } }
class ColoredDimension2     extends Colored2 { // some code here...
  int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } }
Слайд 23

Polymorphism and Generics class Holder { T item; void set(T item)

Polymorphism and Generics
class Holder {   T item;   void set(T item) {

this.item = item; }   T get() { return item; } } abstract class Parent { }
class Child extends Parent { }
class AnotherChild extends Parent { }
...
Holder h1 = new Holder(); // OK
Holder h2 = new Holder(); // OK
Holder h3 = new Holder(); // Error
// because one could do this:
// h3.set(new AnotherChild());
Слайд 24

Why Polymorphism doesn’t work class Holder { T[] items; int num

Why Polymorphism doesn’t work

class Holder {   T[] items;
int num =

0;   void add(T item) { this.items[num++] = item;}   T[] get() { return items; } }
...
Holder h = new Holder();
h.add(1);
h.add(2);
Holder reg = h;
h.add(new Double(1.25));
Integer i3 = h.get()[2]
So Double is a Number but Holder is not Holder

Class case exception Double != Integer

h -> {Integer, Integer, Double}

If polymorphism was allowed this would be legal

This is also legal since Double is a Number

Слайд 25

But you can do this: This is how you can put

But you can do this:

This is how you can put different

object types in parameterized Holder object:
Holder h = new Holder();
h.add(1);
h.add(2);
h.add(new Double(1.25));
Number i3 = h.get()[2]

Both Integer and Double are the Numbers

Слайд 26

More Example abstract class Animal { public abstract void check(); }

More Example
abstract class Animal { public abstract void check(); }
class Dog extends Animal

{
  public void check() { S.o.p("Dog"); }
}
class Cat extends Animal {
  public void check() { S.o.p("Cat"); }
}
class AnimalDoctor {
  void checkAnimal(Holder animal) {
  animal.get().check();
  }
  public static void main(String[] args) {
  Holder dog = new Holder();
  Holder cat = new Holder();
    AnimalDoctor doctor = new AnimalDoctor();
  doctor.checkAnimal(dog); // Error
    doctor.checkAnimal(cat); // Error
  }
}
Слайд 27

Generic Wildcards (?) The wildcard provides a polymorphic - like behavior

Generic Wildcards (?)

The wildcard provides a polymorphic - like behavior for

declaring generics.
, an unbounded wildcard
, a wildcard with an upper bound
, a wildcard with a lower bound
Слайд 28

Unbounded Wildcards The unbounded wildcard represents any data type, similar to

Unbounded Wildcards

The unbounded wildcard represents any data type, similar to the

< T > syntax.
public static void printList(List list) {
for(Object x : list) {
System.out.println(x.toString());
}
}
...
ArrayList keywords = new ArrayList();
kyewords.add(“generic”);
printList(keywords);
Use the ? in situations where you do not need a formal parameter type like < T >

Data type is not required here

Слайд 29

Be careful Holder h = new Holder (); h.add(new Object()); //

Be careful

Holder h = new Holder();
h.add(new Object()); // compile time error
h.add(new

String()); // compile time error
// one exception!
h.add(null); // null is member of every type
Working with unbounded wildcards we can only read data, not assign!
Слайд 30

Upper - Bound Wildcards Bounded wildcards put some restrictions on unknown

Upper - Bound Wildcards

Bounded wildcards put some restrictions on unknown type:
public

static void printList(List list){
for(Number x : list) {
System.out.println(x.doubleValue());
}
list.add(new Integer(3)); // compile error
}

Now we know that object is instance of Number

But we still don’t know exact type, so can’t modify list

Слайд 31

More example class AnimalDoctor { void checkAnimal(Holder animal) { animal.get().check(); //

More example
class AnimalDoctor {
  void checkAnimal(Holder animal) {
  animal.get().check();

// OK
  animal.set(new Cat()); // Error: we don’t know exact parameter type of animal
  }
  public static void main(String[] args) {
  Holder dog = new Holder();
  Holder cat = new Holder();
    AnimalDoctor doctor = new AnimalDoctor();
  doctor.checkAnimal(dog); // OK
    doctor.checkAnimal(cat); // OK
  }
}
Слайд 32

Lower Bounded Wildcards a lower bounded wildcard restricts the unknown type

Lower Bounded Wildcards

a lower bounded wildcard restricts the unknown type to be a

specific type or a super type of that type
Holder h = new Holder();
h.add(new Integer(1));
Integer i1 = h.get(); // compilation error
// get returns Object
Integer i2 = (Integer)h.get(); // OK
Lower bounded wildcards allow to modify but not read!!
Слайд 33

Example public static void addNumbers(List list) { for (int i =

Example

public static void addNumbers(List list) {
for (int i

= 1; i <= 10; i++) {
list.add(i);
}
}
List i = new ArrayList();
List n = new ArrayList();
List o = new ArrayList();
addNumbers(i);
addNumbers(n);
addNumbers(o);
This works fine
Слайд 34

More example class AnimalDoctor { void checkAnimal(Holder dog) { animal.get().check(); //

More example
class AnimalDoctor {
  void checkAnimal(Holder dog) {
  animal.get().check();

// Error!
  // get() returns Object ref
  animal.set(new Dog()); // OK
  }
  public static void main(String[] args) {
  Holder dog = new Holder();
  Holder animal = new Holder();
  Holder cat= new Holder();
    AnimalDoctor doctor = new AnimalDoctor();
  doctor.checkAnimal(dog); // OK
    doctor.checkAnimal(animal); // OK
    doctor.checkAnimal(cat); //Error: Cat isn’t super of Dog
  }
}
Слайд 35

What's the difference? void check(Holder holder) { } void check(Holder holder) { }

What's the difference?
void check(Holder holder) { }
void check(Holder holder) { }
Слайд 36

There IS a huge difference! class Main { void check(Holder obj)

There IS a huge difference!
class Main {
  void check(Holder obj) {
 
obj.set(new Dog());
    obj.set(new Cat());
  }
  public static void main(String[] args) {
  Holder obj = new Holder();
    Main main = new Main();
    main.check(obj); // Only Holder goes here!
  }
}
Слайд 37

There IS a huge difference! class Main { void check(Holder obj)

There IS a huge difference!
class Main {
  void check(Holder obj) {
 

obj.set(new Dog()); // Error
  // Compiler isn't sure that it's really Dog Holder
  }
  public static void main(String[] args) {
  Holder dog = new Holder();
    Holder integer = new Holder();
    Main main = new Main();
    main.check(dog); // OK
    main.check(integer); // OK
  }
}
Слайд 38

Which will compile? 1) List list = new ArrayList (); 2)

Which will compile?
1) List list = new ArrayList();
2) List

aList = new ArrayList();
3) List foo = new ArrayList();
4) List cList = new ArrayList();
5) List bList = new ArrayList();
6) List dList = new ArrayList();
Слайд 39

Which will compile? 1) List list = new ArrayList (); 2)

Which will compile?
1) List list = new ArrayList();
2) List

aList = new ArrayList();
3) List foo = new ArrayList();
4) List cList = new ArrayList();
5) List bList = new ArrayList();
6) List dList = new ArrayList();
Слайд 40

Naming Conventions for Generics E for an element K for a

Naming Conventions for Generics

E for an element
K for a map key
V

for a map value
N for a number
T for a generic data type
Use S , U , V , and so on for multiple types in the same class.
Слайд 41

Summary 1) Generics let you enforce compile-time type safety. Holder h1

Summary
1) Generics let you enforce compile-time type safety.
Holder h1 = new

Holder();
Holder h2 = new Holder();
String s = h1.get(); // no cast needed
String s = (String) h2.get(); // cast required
2) Generic type information does not exist at runtime — it is for compile-time safety only.
3) Polymorphic assignment don't apply to the generic type
Holder h1 = new Holder(); // Error
4) Wildcard syntax allows a generic method, accept subtypes (or supertypes) of the declared type of the method argument:
void foo(List d) {} // can take only
void foo(List) {} // take a or
void foo(List) {} // take a or
5) When using a wildcard, List, the collection can be accessed but not modified.