为什么有泛型?
早期的Object类可以接受任意的对象类型,但是在实际使用中会有类型转换问题。也就存在了安全隐患
class Demo02{
//早期我们使用Object表示任意类型
private Object object;
public Object getObject(){
return this.object;
}
public void setObject(Object object){
this.object = object;
}
}
public class Demo {
public static void main(String[] args) {
//正常使用
Demo02 demo02 = new Demo02();
demo02.setObject(new Integer(19));
Integer i = (Integer)demo02.getObject();
System.out.println("年龄是:" + i +"岁。");
/**
* 向上转型是没有问题的,但是向下转型就会隐含类型转换问题了
* 也就是说这样的程序并不安全。
*/
Demo02 d = new Demo02();
d.setObject(new String("hello"));
//我们传入的是Integer类型,但是我们转换为String类型,编译器虽然不报错,但是运行就会出错
Integer i2 = (Integer)d.getObject(); //ClassCastException异常
System.out.println("年龄是:" + i2 +"岁。");
}
}
所以JDK5后java提供了泛型来解决这个问题。
泛型类的使用
//尖括号里面的字母可以随便写
class Demo02<T>{
private T t;
public T getObject(){
return this.t;
}
public void setObject(T t){
this.t = t;
}
}
public class Demo{
public static void main(String[] args) {
//正常使用
Demo02<Integer> demo02 = new Demo02();
demo02.setObject(new Integer(19));
Integer i = demo02.getObject();
System.out.println("年龄是:" + i +"岁。");
/**
* 我们已经固定了传入的类型必须为Integer类型
* 此时再传入String类型的话就会编译报错了
*/
Demo02<Integer> d = new Demo02();
d.setObject(new String("hello")); //编译报错
Integer i2 = (Integer)d.getObject();
System.out.println("年龄是:" + i2 +"岁。");
}
}
泛型方法
有这样一个需求,我们想要使用一个方法名输出不同的类型的数据,那么就需要重载这个方法,要是我的需求需要有几十个数据类型,那么我就需要写几十个重载的方法,这样的代码太臃肿了。如下:
public class Demo{
public static void main(String[] args) {
Demo02 d1 = new Demo02();
d1.show("hello"); //输出字符串
d1.show(100); //输出数字
d1.show(99.99); //输出小数
}
}
class Demo02{
/**
* 我们想要使用一个方法名输出不同的类型就需要重载这个方法
* */
//输出字符串
public void show(String str){
System.out.println(str);
}
//输出数字
public void show(int i){
System.out.println(i);
}
//输出小数
public void show(double d){
System.out.println(d);
}
// 输出其他类型......
}
所以我们可以使用泛型方法来解决这个问题:
class Demo02{
/**
* 只需要写一个方法即可,需要注意的是如果类上面不写泛型的话
* 在方法的括号里面写泛型那么就需要在返回值类型的前面加 <泛型名>
* */
//输出
public <T> void show(T t){ //需要在返回值前面加 <T> ,T是你的泛型名
System.out.println(t);
}
}
public class Demo{
public static void main(String[] args) {
Demo02 d1 = new Demo02();
d1.show("hello"); //输出字符串
d1.show(100); //输出数字
d1.show(99.99); //输出小数
}
}
泛型接口
接下来我们看看泛型接口:
定义一个泛型接口:
public interface Demo<T>{
void show(T t);
}
通过类去实现这个泛型接口的时候指定泛型T的具体类型。
指定具体类型为String:
public class DemoImpl implements Demo<String>{
@Override
public void show(String t) {
System.out.println(t);
}
public static void main(String[] args) {
Demo<String> demo = new DemoImpl(); //明确指定为String类型
demo.show("hello");
demo.show(100); //这里我们传入int类型就会遍编译报错了
}
}
上面直接指定泛型的数据类型,这种方式并不常见,最常见的是使用的时候才指定泛型的数据类型,如下,还是实现Demo接口:
/**
* 这里需要注意的是,如果你想在使用的时候才指定泛型的数据类型
* 那么就必须在实现接口类上面也加上泛型的名字,
* 且泛型接口不能直接指定泛型的数据类型
* */
public class DemoImpl<T> implements Demo<T>{
@Override
public void show(T t) {
System.out.println(t);
}
public static void main(String[] args) {
/**
* 实现类的数据类型要和泛型接口的数据类型一致
* 否则会报错
* */
Demo<String> demo = new DemoImpl<String>();
demo.show("hello");
Demo<Integer> demo2 = new DemoImpl<Integer>();
demo2.show(100);
/**
*这里还需要注意就是如果右边的泛型接口不写数据类型的话,并不会报错
*但是你要是传入其他的数据类型他都能正常输出,那样的话就达不到我们想要的效果了
*/
Demo demo3 = new DemoImpl<String>(); //不指定泛型接口Demo的数据类型
demo3.show("hello");
demo3.show(1000); //虽然实现类固定了为String类型但是此处编译不报错,运行也能正常输出
}
}
泛型高级(通配符)
<?> 表示任意类型,如果没有明确规定就是Object类型以及任意的java类型
<? extends E> 向下限定,E及其子类
<? super E> 向上限定,E及其父类