从JDK1.5以后引入了三大常用新特性:泛型(Genericity)、枚举(enum)、注解(Annotation)。其中在JDK1.5中泛型是一个非常重要的实现技术,它可以帮助我们解决程序的参数转换问题。
泛型假设需要定义一个描述坐标的程序类Point,需要提供两个属性x、y。对于这两个属性的内容可能有如下选择:
x = 10、y = 20 ;
x = 10.1、y = 20.1 ;
x = 东经80度、y = 北纬20度
那么现在首先要解决的问题就是Point类中的x、y的属性类型问题,此时需要保存的有int、double、String,所以 在java中只有一种类型可以保存所有类型:Object型
这个时候由于设置方的错误,将坐标内容设置成了double与String,但是接收方不知道,于是在执行时就会出现 ClassCastException。 ClassCastException指的是两个没有关系的对象进行强转出现的异常。
这个时候语法不会对其做任何限制,但执行的时候出现了程序错误,所以得出结论:向下转型是不安全的操作,会带来隐患。
泛型类的基本使用
泛型指的就是在类定义的时候并不会设置类中的属性或方法中的参数的具体类型,而是在类使用时再进行定义。
如果要想进行这种泛型的操作,就必须做一个类型标记的声明。
class MyClass<T> {
T value1;
}
尖括号 <> 中的 T 被称作是类型参数,用于指代任何类型。实际上这个T你可以任意写,但出于规范的目的,Java 还是建议我们用单个大写字母来代表类型参数
泛型方法
泛型不仅可以用于定义类,还可以单独来定义方法。如下所示:
class MyClass{
public <T> void testMethod(T t) {
System.out.println(t);
}
}
泛型方法与泛型类稍有不同的地方是,类型参数也就是尖括号那一部分是写在返回值前面的。 中的 T 被称为 类型参数,而方法中的 T 被称为参数化类型,它不是运行时真正的参数。
当然,声明的类型参数,其实也是可以当作返回值的类型的。
class MyClass{
public <T> T testMethod(T t) {
return t;
}
}
泛型应用到栈,接收任意数据类型
之前在应用栈解决括号匹配问题时,为了接收任意类型的数据,将内部设置成了Object,这样虽然解决了任意数据存储问题,但是,随之也带来了问题,之前入栈的是字符,取出来却变成了对象,为了得到字符还得采用一系列复杂的操作,先将其转换成字符串,再转换成字符,曲线救国,char c = stack.stackTop().toString().charAt(0);
虽然采用这样的方式能得到其中的内容,但是其中存在数据安全问题,假如栈已经存的不再是字符,再对其进行转换,那么就会得到一个错误的结果。
那么,将泛型引入,最主要的用途就是解决这个类型转换问题
在接口:
interface Stack <T>{
void stackPush(T obj);//入栈
void stackPop();//出栈
int size();//栈元素数量
T stackTop();//取栈顶
void printStak();//辅助函数,打印栈中元素
}
以及实现接口时:
class StackImp <T> implements Stack <T>{
都采用了泛型,这样做的好处就是,在实例化栈这个对象时,我只需要告诉类我要实例化的对象时什么类型就行了:
Stack<Character> stack = new StackImp<Character>();
这样就不用再顾虑对象取出类型转换问题,一切变得美好而和谐。
栈中应用泛型并实现括号匹配
具体相关栈以及应用栈括号匹配:>JAVA栈应用之括号匹配<、>JAVA实现动态栈<
package stack;
interface Stack <T>{
void stackPush(T obj);//入栈
void stackPop();//出栈
int size();//栈元素数量
T stackTop();//取栈顶
void printStak();//辅助函数,打印栈中元素
}
class StackImp <T> implements Stack <T>{
//栈中要有啥?1.元素数量(头节点),里面放栈头和尾巴
//栈的每个节点放元素和下一个节点的位置
private int size;
private Node first;
private Node last;
//建一个内部类,实例化节点
private class Node {//只能此内部访问,private修饰,增强安全性
private T item;
private Node next;
private Node(T item ,Node next) {//
this.item = item;
this.next = next;
}
}
@Override
public void stackPush(T obj) {
Node tmp = this.first;
Node newNode = new Node(obj, null);
this.last = newNode;
if (null == this.first) {//首次时
this.first = newNode;
}else {
while (null != tmp.next) {
tmp = tmp.next;
}
tmp.next = newNode;
}
++ this.size;
}
@Override
public void stackPop() {
Node tmp = this.first;
if (null == tmp.next) {
this.first =null;
this.last = null;
this.size = 0;
return ;
}
while (null !=tmp.next.next) {
tmp = tmp.next;
}
this.last = tmp;
tmp.next = null;
--this.size;
}
@Override
public int size() {
return this.size;
}
@Override
public T stackTop() {
return (T) this.last.item;//不是返回return this.last啊
}
@Override
public void printStak() {
Node tmp = this.first;
if (null == this.first) {
return;
}
while (null != tmp.next) {
System.out.print(tmp.item + "->");
tmp = tmp.next;
}
System.out.println(tmp.item);
}
}
class Factory{
public static Stack<Character> getStack() {
return new StackImp<Character>();
}
}
public class Test {
public static void main(String[] args) {
// Stack<Character> stack = Factory.getStack();//工厂产生栈对象
Stack<Character> stack = new StackImp<Character>();//自己产生栈对象
String str = "(())hello Jan{{}}";
bracket(stack, str);
//括号匹配:左边括号入栈,右边括号,取栈,是一对,pop,不是,return wrong
//可能会用到 字符串 转字符数组 char [] strarr = str.toCharArray();
//++++++++++栈检测++++++++++
// System.out.println(stack.size());
// stack.stackPush1("1");
// stack.stackPush1("2");
// stack.stackPush1("3");
// stack.stackPush1("4");
// stack.stackPush1("5");
// stack.stackPop();
// System.out.println(stack.size());
// stack.printStak();
}
public static void bracket (Stack<Character> stack,String str) {
if (null == str) {
return ;
}
char [] strarr = new char [str.length()];//这里的length()区别于数组中的length,前是方法,后是数组变量
strarr = str.toCharArray();//直接就放进去了
int i = 0;//大小写转换 CTRL+ shift x/y
while (i < strarr.length) {
//先判断是不是括号,是左括号,入栈
if ('(' == strarr[i] || '[' == strarr[i] || '{' == strarr [i]) {
stack.stackPush(strarr[i]);
++ i;//前置效率高,不产生临时变量
continue;//入栈后面就不用再走了
}
if (')' == strarr[i] || ']' == strarr[i] || '}' == strarr [i]) {//是右边括号
if(stack.size() == 0) {//栈空了,还来右边括号,那右边括号多
System.out.println("右括号多");
++ i;
return;
}
char c = stack.stackTop();//走起.toString().charAt(0) ;
if ((c == '(' && ')'== strarr[i]) || ( c == '[' && ']' == strarr[i]) || ( c == '{' && '}' == strarr[i])){
stack.stackPop();//栈中的括号与当前匹配
++ i;
continue;
}else {//此时栈中与当前括号不匹配//[}
System.out.println("括号不匹配");
++ i;
return;
}
}
++ i;
}
if(stack.size() != 0) {//循环结束,要么完全匹配(栈空),要么左边括号多(栈不空),
System.out.println("左括号多");
return;
}
System.out.println("匹配 =.=");
}
}
>源码<
泛型的出现彻底改变了向下转型的需求。引入泛型后,如果明确设置了类型,则为设置类型:如果没有设置类型,则默认为Object类型。