目录
泛型概念引入
有一个问题,如果我们需要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员的类型不同。那么我们如何去做?
我们看下面代码:
class Cls1
{
int a;
public Cls1(int a){
this.a = a;
}
public int getData(){
return a;
}
}
class Cls2
{
String a;
public Cls2(String a){
this.a = a;
}
public String getData(){
return a;
}
}
public class Test {
public static void main(String[] args) {
Cls1 cls1 = new Cls1(10);
System.out.println(cls1.getData());
Cls2 cls2 = new Cls2("yangchenyang");
System.out.println(cls2.getData());
}
}
这段代码中有两个类,这两个类除了变量的类型不同,其他的部分都相同。如果我们现在还需要更多的变量类型不同,但实现逻辑相同的类的话,那么我们就需要定义更多的类,很容易看出这样会使代码变得冗杂,导致类的膨胀,重用性太差。
既然这样,我们就有另一种方法:即创建一个类文件,给这个类中的成员变量设置Object数据类型。
class Cls
{
Object a;
public Cls(Object a){
this.a = a;
}
public Object getData(){
return a;
}
}
public class Test {
public static void main(String[] args) {
Cls cls1 = new Cls(10);
System.out.println(cls1.getData());
Cls cls2 = new Cls("yangchenyang");
System.out.println(cls2.getData());
}
}
这种方法利用了Object是所有类型的父类的特点,可以将它根据需要随意进行转换,但是它也有缺点:编译的时候正常,但运行的时候可能会异常。
对于这种情况的发生,我们有很好的解决办法,就是泛型。
泛型简介(JDK1.5以后)
- 泛型可以在编译的时候检查类型安全,并且所有的强制类型转换都是自动和隐式的。
- 泛型的原理就是“数据的参数化”,即把类型看作整数。也就是说把所要操作的数据类型看作参数,就像方法的形式参数是运行时传递的值的占位符一样。
- 简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。
- 泛型可以提高代码的扩展性和重用性。
根据泛型的用法,上面的代码可以再进行修改:
class Cls<T>
{
T a;
public Cls(T a){
this.a = a;
}
public T getData(){
return a;
}
}
public class Test {
public static void main(String[] args) {
Cls<Integer> cls1 = new Cls<Integer>(10);
System.out.println(cls1.getData());
Cls<String> cls2 = new Cls<String>("yangchenyang");
System.out.println(cls2.getData());
}
}
泛型类及特点
- 泛型的类型参数可以是泛型类
class Cls<T>
{
T a;
public Cls(T a){
this.a = a;
}
public T getData(){
return a;
}
}
public class Test {
public static void main(String[] args) {
//泛型类里的类型还是泛型,构造方法参数应该为泛型类实例化后的对象
Cls<Cls<Integer>> cls1 = new Cls<Cls<Integer>>(new Cls<Integer>(100));
System.out.println(cls1.getData().getData()); //实则为泛型的嵌套
}
}
- 泛型类可以同时设置多个类型参数
class Cls1<T, T1>
{
T a;
T1 b;
public Cls1(T a, T1 b){
this.a = a;
this.b = b;
}
public T getData(){
return a;
}
public T1 getData1(){
return b;
}
}
public class Test {
public static void main(String[] args) {
Cls1<Integer, String> cls3 = new Cls1<Integer, String>(100, "帅"); //两个泛型类型的实例化
System.out.println(cls3.getData()+cls3.getData1()); //字符串和整型相加是连接
//System.out.println(cls3.getData1());
Cls1<Integer, Integer> cls4 = new Cls1<Integer, Integer>(100, 10);
System.out.println(cls4.getData()+cls4.getData1());
//System.out.println(cls3.getData1());
}
}
- 泛型类可以继承泛型类
- 泛型类可以实现泛型接口
abstract class Cls<T>
{
T a;
public Cls(T a){
this.a = a;
}
public T getData(){
return a;
}
abstract void printInfo();
}
interface Cls2<T>
{
abstract void printInfoCls2(T t);
}
class Cls1<T, T1> extends Cls<T> implements Cls2<T>
{
T1 b;
public Cls1(T a, T1 b){
super(a); //先调用父类的构造方法
this.b = b;
}
public T1 getData1(){
return b;
}
void printInfo(){ //实现父类中的抽象方法
System.out.println("父类抽象方法的实现");
}
public void printInfoCls2(T t) { //实现接口中的方法
// TODO Auto-generated method stub
System.out.println(t);
}
}
public class Test {
public static void main(String[] args) {
Cls1<Integer, String> cls2 = new Cls1<Integer, String>(10, "yangchenyang");
System.out.println(cls2.getData());
System.out.println(cls2.getData1());
cls2.printInfo(); //调用实现了的父类抽象方法
cls2.printInfoCls2(100); //调用实现了的接口中的方法
}
}
限制泛型可用类型
在定义泛型类别时,默认在实例化泛型类的时候可以使用任何类型,但是如果想要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。
当没有指定泛型继承的类型或接口时,默认使用extends Object,所以默认情况下任何类型都可以作为参数传入。
class Animal
{
}
class Dog extends Animal
{
}
interface Move
{
abstract void test();
}
abstract class Cls<T extends String> //限制泛型可用类型为String
{
T a;
public Cls(T a){
this.a = a;
}
public T getData(){
return a;
}
abstract void printInfo();
}
interface Cls2<T>
{
abstract void printInfoCls2(T t);
}
class Cls1<T extends String, T1 extends Dog> extends Cls<T> implements Cls2<T> //限制泛型T1继承Dog类
{
T1 b;
public Cls1(T a, T1 b){
super(a); //先调用父类的构造方法
this.b = b;
}
public T1 getData1(){
return b;
}
void printInfo(){ //实现父类中的抽象方法
System.out.println("父类抽象方法的实现");
}
public void printInfoCls2(T t) { //实现接口中的方法
// TODO Auto-generated method stub
System.out.println(t);
}
}
public class Test {
public static void main(String[] args) {
Cls1<String, Dog> cls2 = new Cls1<String, Dog>("chenyangchen", new Dog()); //被限制的泛型的参数
}
}
注意:泛型继承接口的时候要用关键字 extends。
泛型通配的方式
类型通配声明
同一泛型类,如果实例化给定的实际类型不同,则这些实例的类型时不兼容的,不能相互赋值。
Generic<Boolean>f1 = new Generic<Boolean>();
Generic<Integer>f2 = new Generic<Integer>();
f1 = f2; //发生编译错误
Generic<Object>f = f1; //f1和f类型并不兼容,发生编译错误
f = f2; //f1和f类型同样不兼容,也会发生编译错误
泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型通配符(?)声明泛型类的变量就可以解决这个问题。
泛型通配的方式
- ”?“表示任意一个类型
Generic<Boolean>f1 = new Generic<Boolean>();
Generic<?> f = f1;
- 和限制泛型的上限相似,同样可以使用extends关键字限定通配符的上限
Generic<Dog>f1 = new Generic<Dog>();
Generic<? extends Animal> f = f1;
- 还可以使用super关键字将通配符匹配类型限定为某个类型及其父类型
Generic<Animal>f1 = new Generic<Animal>();
Generic<? super Dog> f = f1;
案例代码
class Animal
{
}
class Dog extends Animal
{
}
class Cls<T> //限制泛型可用类型为String
{
T a;
public Cls(T a){
this.a = a;
}
public T getData(){
return a;
}
}
public class Test {
public static void main(String[] args) {
Cls<Integer> c1 = new Cls<Integer>(10);
Cls<Double> c2 = new Cls<Double>(10.0);
Cls<? extends Animal> c3;
Cls<Dog> c4 = new Cls<Dog>(new Dog());
c3 = c1; //编译报错
c3 = c2; //编译报错
c3 = c4; //c3可以通配Animal的子类
}
}
泛型方法
用法和特点
- 不仅类可以声明类型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:
访问修饰符 <泛型列表> 返回类型 方法名(参数列表){
实现代码
}
- 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。
- 类中其他方法不能使用当前方法声明的类型。
提示:是否拥有泛型方法,于其所在的类是否有泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。
例程代码
class A<T>
{
public void printInfo(T t){
System.out.println(t);
}
}
class B
{
public <T> T printInfo(T t){ //这里是泛型方法,返回值可以为泛型类型
System.out.println(t);
return t;
}
public <T, T2> void printInfo(T t, T2 t2){ //这里是泛型方法的重载
System.out.println(t);
System.out.println(t2);
// System.out.println(t2); //报错,不让加,因为没有实例化,不能确定泛型的类型
}
}
public class Test {
public static void main(String[] args) {
A<String> a = new A<String>();
a.printInfo("哈哈"); //已经限定参数类型为字符串,不能打印其他的
//a.printInfo(1234); //已经限定参数类型为字符串,报错
B b = new B();
b.printInfo("哈哈"); //泛型方法类似于方法的重载,但比其要更灵活
b.printInfo(1234);
b.printInfo(0.5);
b.printInfo('c');
}
}
什么时候使用泛型方法,而不使用泛型类呢?
- 添加类型约束只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。
- 施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。
泛型方法限制泛型可用类型
class Animal
{
public void eat(){
System.out.println("动物吃");
}
}
class Dog extends Animal
{
public void eat(){
System.out.println("啃骨头");
}
}
class B
{
public static <T extends Animal> void printInfo2(T t){ //泛型方法中的类型约束,这种方法被称为静态方法
t.eat();
}
}
public class Test {
public static void main(String[] args) {
B b = new B();
b.printInfo2(new Dog());
B.printInfo2(new Dog());
}
}