我们知道Object类可以接收任何类型的类,如果描述一个坐标可以用数字描述,也可以用字符串描述(东经 北纬…),Object根据不同的需求接收不同的类可以完成一些操作:
class Position
{
private Object x;
private Object y;
public void setX(Object x) {
this.x = x;
}
public void setY(Object y) {
this.y = y;
}
public Object getX() {
return x;
}
public Object getY() {
return y;
}
}
public class T1
{
public static void main(String[] args)
{
//假设位置是double型
Position p1=new Position();
p1.setX(2.2); //自动装箱向上转型为Object
p1.setY(3.4);
double x1=(double)p1.getX(); //强转向下转为Double,然后自动拆箱为double
double y1=(Double)p1.getY();
System.out.println(x1+"、" + y1);
//假设位置是String类型
Position p2=new Position();
p2.setX("东经32"); //向上转型为Object
p2.setY("北纬50");
String x2=(String)p2.getX(); //Object类向下转型为一个具体的类String
String y2=(String)p2.getY();
System.out.println(x2+"、"+y2); //东经32、北纬50
}
}
如上,Object可以接收客户端输入的任意类型数据,但是也正是因为这个原因导致了一些问题:
Position p3=new Position();
p3.setX(3.4);
p3.setY("北纬50");
String x3=(String)p3.getX();
String y3=(String)p3.getY();
System.out.println(x3+"、"+y3);
为什么会有泛型?
上面代码并不会明显提示客户端已经出现错误,但是运行时,会有运行时错误—数值转换异常ClassCastException(两个没有关系的对象进行强转出现的异常),我们就可以明显看到,在Object向下转型时会有不安全操作,为了避免这种不安全性,有了泛型。
泛型是什么?
泛型指的就是在类定义的时候并不会设置类中的属性或方法中的参数的具体类型,而是在类使用时再进行定义。
泛型类:
泛型类指的是类在定义时并不设置类的属性类型,而是在使用时再定义。
定义一个泛型类:
class Myclass <T>
{
T vavule1;
}
表示类型参数,可以指代任意类型。一般会用大写字母表示类型参数。
T 代表一般的类
E :泛型中类的属性 elemen
K : key
V :Value ,常与K一起搭配使用
K/V 称为键值对(间Map集合)
//使用泛型类,在使用时定义类类型
MyClass<Integer> c1=new MyClass<>(); //后面的类型可以省
MyClass<String> c2=new MyClass<String>();
泛型只允许接受类,所有基本类型必须使用包装类(看什么是引用类型)前面不可省,后面可省(JDK 1.7)
当然泛型类可以有多个泛型参数:
class MyClass<T,E> //2个泛型参数
{
T value1;
E value2;
}
MyClass<String,Integer> myClass1 = new MyClass<String,Integer>();
引入泛型类后,对于Position类可以改为:
///////用泛型类
class Position<T>
{
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
}
public class T1
{
public static void main(String[] args)
{
Position<Integer> p1=new Position<>(); //定义后类型参数T为String类
p1.setX(10);
p1.setY(20);
int x1=p1.getX(); //没有强转,这时T已经是String类
int y1=p1.getY(); //引入泛型后,避免了向下转型
System.out.println(x1+"、"+y1);
Position<String> p2=new Position<>();
p2.setX("东经32");
p2.setY("北纬50");
String x2=p2.getX();
String y2=p2.getY();
System.out.println(x2+"、"+y2);
}
}
并在引入泛型后,在使用类时定义类型,当类型定后,如果客户端输入的类型不对,会立即告诉用户有错,即强制用户修改:
泛型类的优点:
1.避免向下转型;
2.可是有效提醒用户输入错误(比如在输入账号时,如果用户输入字母,则会有账号字符是数字,不是字母之类的提示)。
注:引入泛型后,如果明确设置了类型,则为设置类型;如果没有设置类型,则默认为Object类型。
泛型方法
定义一个泛型方法:
public <T> void MyMethod(T t)
{
System.out.println(t);
}
<T> 表示是泛型方法 没有<T> 则是普通方法
返回值是void 如果需要返回值是泛型,将void 改为T
泛型类和泛型方法共存:
class A<T>
{
private T a;
private T b;
public <T> T print1(T t) //泛型方法,泛型方法中泛型参数和泛型类的泛型参数没有关系
{
System.out.println(t);
return t;
}
public void print2(T t) //普通方法
{
System.out.println(t);
}
}
public class T1 {
public static void main(String[] args)
{
A<String> a1=new A<String>();
a1.print2("pick"); //普通方法
int ret=a1.print1(10); //返回值是Integer,自动拆箱
//如果泛型方法 泛型参数是Integer,泛型类泛型参数是String,泛型类的泛型参数和泛型方法泛型方法无关
}
}
泛型类中的类型参数与泛型方法中的类型参数是没有相应的联系的,泛型方法始终以自己定义的类型参数为准。但是泛型类的类型参数和成员变量的类型保持一致。
编码规范:泛型方法类型参数不要和泛型类的类型参数不要同名。
泛型接口:
定义一个泛型接口:
interface IA<T> //泛型接口
{
public void func(T t);
}
子类实现接口有两种方式:
- 子类继续保持泛型
class A1Impl<T> implements IA<T> //前面<T>表示是泛型类,后面T是接口泛型参数 两个T是一样的
{
}
- 子类在定义时确定好类型
class A2Impl implements IA<String> //确定泛型参数为String
{
}
泛型接口具体实现:
interface IA<T> //泛型接口
{
public void func(T t);
}
//子类继续保持泛型
class A1Impl<T> implements IA<T>
{
public void func(T t)
{
System.out.println(t);
}
}
//子类在定义时确定好类型
class A2Impl implements IA<Integer>
{
public void func(Integer data)
{
System.out.println(data);
}
}
public class T1 {
public static void main(String[] args)
{
IA<String> a1=new A1Impl<>(); //子类继续保持泛型
a1.func("hello");
IA a2=new A2Impl(); //子类定义时确定泛型参数
a2.func(10);
}
}