// 以下不带泛型的代码需要强制转换:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
// 当使用泛型重写时,代码不需要强制转换:
List list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // no cast
public class Pair {
private T first;
private T second;
public Pair() {
first = null;
second = null;
}
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public T getSecond() {
return second;
}
public void setFirst(T first) {
this.first = first;
}
public void setSecond(T second) {
this.second = second;
}
}
class PairTest1 {
public static void main(String[] args) {
String[] words = {"Mary", "had", "a", "little", "lamb"};
Pair mm = ArrayAlg.minmax(words);
System.out.println("min = " + mm.getFirst()); // min = Mary
System.out.println("max = " + mm.getSecond()); // max = little
}
}
class ArrayAlg {
public static Pair minmax(String[] a) {
if (a == null || a.length == 0) return null;
String min = a[0];
String max = a[0];
for (int i = 0; i < a.length; i++) {
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
}
2.2 泛型方法
以下定义了一个带有类型参数的方法:
class ArrayAlg {
public static T getMiddle(T... a) {
return a[a.length / 2];
}
}
public T put (K key, V value) {
return cache.put(key value);
}
3 类型变量的限定
有时候类或方法需要对类型变量加以约束:
class ArrayAlg {
public static T min(T[] a) {
if (a == null || a.length == 0) return null;
T smallest = a[0];
for (int i = 0; i < a.length; i++) {
if (smallest.compareTo(a[i]) > 0) smallest = a[i];
}
return smallest;
}
}
public class ArrayList {
private Object[] elements;
@SuppressWarnings("unchecked") public E get(int n) { return (E) elements[n]; }
public void set(int n, E e) { elements[n] = e } // no cast needed
}
但实际的实现没有这么清晰:
public class ArrayList {
private E[] elements;
public ArrayList() {
elements = (E[]) new Object[10];
}
}
public class Singleton {
private static T singleInstance; // error
public static T getSingleInstance() { // error
if (singleInstance == null) { // construct new instance of T
return singleInstance;
}
}
}
public class Test {
public static void main(String[] args) {
var thread = new Thread(Task.asRunnable(() -> {
Thread.sleep(1000);
System.out.println("Hello World!");
throw new Execption("Check this out!");
}));
thread.start();
}
}
Pair managerBuddies = new Pair<>(ceo, cfo);
Pair rawBuddies = managerBuddies; // OK
rawBuddies.setFirst(new File("...")); // only a compile-tiem warning
public static void printBuddies(Pair p) {
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + " and " +second.getName() + " are buddies.")
}
不能将Pair传递给这个方法,这一点很受限制。解决的方法很简单:使用通配符类型
public static void printBuddies(Pair extends Employee> p)
类型Pair是Pair extends Employee>的子类型:
使用通配符会通过Pair extends Employee>引用破坏Pair吗?
Pair managerBuddies = new Pair<>(ceo, cfo);
Pair extends Employee> wildcardBuddies = managerBuddies; // OK
wildcardBuddies.setFirst(lowlyEmployee); // compile-time error
public static void minmaxBonus(Manager[] a, Pair super Manager> result) {
if (a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++ ) {
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}
直观的讲,带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
下面是超类型限定的另一种应用。**Comparable接口本身就是一个泛型类型。**声明如下:
public interface Comparable {
public int compareTo(T other);
}
在此,类型变量指示了other参数的类型。例如:String类实现Comparable,它的compareTo方法被声明为public int compareTo(String other),显式的参数有一个正确的类型。接口是一个泛型接口之前,other是一个Object,并且这个方法的实现需要强制类型转换。
由于Comparable是一个泛型类型,也许可以把ArrayAlg类的min方法做的更好一些?可以这样声明:public static > T min(T[] a):
public static void maxminBonus(Manager[] a, Pair super Manager> result) {
minmaxBonus(a, result);
PairAlg.swapHelper(result); // OK--swapHelper captures wildcard type
}
限定通配符对类型进行了限制。有两种限定通配符,一种是 extends T>,它通过确保类型必须是T的子类来设定类型的上界,另一种是 super T>,它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型进行初始化,否则会导致编译错误。>表示了非限定通配符,因为>可以用任意类型来替代。
问:List extends T>和List super T>之间有什么区别?
List extends T>可以接受任何继承自T类型的List,而List super T>可以接受任何T的父类构成的List。例如,List extends Number>可以接受List或List。
问:编写一段泛型程序来实现LRU缓存(最近最少使用)
缓存的大小是固定的,并且支持get和put操作,当缓存已满时,put操作将删除最近最少使用的缓存。
LRU高速缓存可以使用两个数据结构HashMap和一个用于存储数据的双向链表来实现
class Node {
T key;
U value;
Node prev;
Node next;
public Node(T key, U value, Node prev, Node next) {
this.key = key;
this.value = value;
this.prev = prev;
this.next = next;
}
}
class LRUCache {
private Node lru;
private Node mru;
private Map> container;
private int capacity;
private int currentSize;
public LRUCache(int capacity) {
this.capacity = capacity;
this.currentSize = 0;
lru = new Node<>(null, null, null, null);
mru = lru;
container = new HashMap<>();
}
public V get(K key) {
Node tempNode = container.get(key);
if (tempNode == null) {
return null;
} else if (tempNode.key == mru.key) {
return mru.value;
}
Node nextNode = tempNode.next;
Node prevNode = tempNode.prev;
if (tempNode.key == lru.key) {
nextNode.prev = null;
lru = nextNode;
} else if (tempNode.key != mru.key) {
prevNode.next = nextNode;
nextNode.prev = prevNode;
}
tempNode.prev = mru;
mru.next = tempNode;
mru = tempNode;
mru.next = null;
return tempNode.value;
}
public void put(K key, V value) {
if (container.containsKey(key)) {
return;
}
Node myNode = new Node(key, value, mru, null);
mru.next = myNode;
container.put(key, myNode);
mru = myNode;
if (currentSize == capacity) {
container.remove(lru.key);
lru = lru.next;
lru.prev = null;
} else if (currentSize < capacity) {
if (currentSize == 0) {
lru = myNode;
}
currentSize++;
}
}
}
问:可以把List传递给一个接受List
不可以,编译错误,因为List
List
问:Java中List>和List
List>是一个未知类型的List,而List
List> listOfAnyType;
List listOfObject = new ArrayList<>();
List listOfString = new ArrayList<>();
List listOfInteger = new ArrayList<>();
listOfAnyType=listOfString; // legal
listOfAnyType=listOfInteger; // legal
listOfObject=listOfString; // compiler error - in-convertible types