版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
目录
为什么需要泛型:
1、为了编写可以应用于多种类型的代码,使代码更直接和优雅。
2、java单继承体系和接口仍然使程序的约束性太强(主要是受限于特定的类型)
一、使用泛型——实现更加灵活的容器
1、使用Object实现
1.1、首先创建一个“Apple”对象
public class Apple {}
1.2、为了使一个类的应用不局限于一个类型,可以使用Object的方式
public class UserObject {
private Object object;
public UserObject(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public static void main(String[] args) {
UserObject userObject = new UserObject(new Apple());
// 这个方法不能自动的识别类型,如果不进行强转,得出仍然是一个Object
Apple apple = (Apple) userObject.getObject();
// 在已经传入了确定对象的情况下,UserObject的参数仍然可以进行随意的变化
// 这不过是因为在java中所有的对象都是Object,Apple也是其中一个,所以并不能确定明确的类型使用
userObject.setObject("这不是一个Apple对象");
}
}
思考:
(1)使用Object的方式可以实现类和方法的复用,但是编译器却不能识别存储的具体对象,每次都需要进行类型的强转
(2)即使传入了具体对象,这个类的存储类型也不会具体的确认下来
2、使用泛型实现
使用泛型,如果想持有什么类型的对象,将其置于尖括内,此时该泛型的类型就自动的确认了下来。
public class UserGenetic<T> {
private T genetic;
public UserGenetic(T genetic) {
this.genetic = genetic;
}
public T getGenetic() {
return genetic;
}
public void setGenetic(T genetic) {
this.genetic = genetic;
}
public static void main(String[] args) {
UserGenetic<Apple> userGenetic = new UserGenetic<>(new Apple());
// 不再需要进行强转
Apple genetic = userGenetic.getGenetic();
// userGenetic.setGenetic("这不是Apple对象"); 编译不能通过,因为String不是Apple对象
}
}
二、简单的容器实例
1、重新写一个List集合
简单重写一个List,然后满足任意对象简单的存值和取值功能:
public class NewList<T> {
// 底层通过list来实现的新集合
private List<T> list = new ArrayList<>();
public void add(T item){
System.out.println("这是来自新集合的存储逻辑..." + item);
list.add(item);
}
public T select(Integer index){
System.out.println("这是来自新集合的取值逻辑..." + index);
return list.get(index);
}
public static void main(String[] args) {
NewList<String> list = new NewList<>();
for(String str :"这 是 一 个 新 集 合".split(" ")){
list.add(str);
}
System.out.println(list.select(0));
}
}
有没有发现,其实有点像List集合的源码,源码基本上都不指定确定的类型,从而能创建出更加灵活和通用的工具类。体会到泛型的强大和好处了吧,其实,泛型也就是另一种类型罢了。
2、实现内部链接存储机制——堆栈
public class LinkedStack<T> {
// 存储节点
private static class Node<E> {
/**
* 泛型对象
*/
private E item;
/**
* 节点本身
*/
private Node<E> next;
// 初始化链式存储表为空
public Node() {
item = null;
next = null;
}
// 构造方法,注意参数包含了节点本身对象,这个是链式的关键
public Node(E item, Node<E> next) {
this.item = item;
this.next = next;
}
// 哨兵判空模式,结合取值时使用,即建立一个结束点或者条件
// 当碰到这个条件时,说明已经达到容器末端
boolean end() {
return item == null && next == null;
}
}
// 这是一个哨兵,末端的值为空
private Node<T> top = new Node<T>();
// 注意,第一次添加时,top是一个哨兵
public void push(T item) {
top = new Node<T>(item, top);
}
//
public T pop() {
// 获取第一个参数的值(泛型Item)
T result = top.item;
// 如果item 和 next 不为空,把next放到原item的位置,以此循环,一个一个取值
if (!top.end()) {
top = top.next;
}
return result;//最后返回哨兵的item == null;
}
public static void main(String[] args) {
LinkedStack<String> lss = new LinkedStack<>();
for (String s : "a b c d e f g".split(" ")) {
lss.push(s);
}
String s;
while ((s = lss.pop()) != null) {
System.out.print(s);
// 打印结果:gfedcba 先进后出的堆栈
}
}
}
使用泛型来实现内部链式存储机制,关键在于容器本身包含了一个自己对象,然后另一个对象又是自身对象的延伸,当不断的去存储值时,实际是在不断的新建对象,只是这个新建的对象包含了之前建立的对象的值,因此,这个容器最后看起来就像一条长长的链子,一环扣一环。