Java-泛型

1. 集合中的泛型

不使用泛型带来的问题:

  • ADHero(物理攻击英雄) APHero(魔法攻击英雄)都是Hero的子类。
  • ArrayList默认接受Object类型的对象,所以所有对象都可以放进ArrayList中。所以get(0)返回的类型是Object。
  • 接着,需要进行强制转换才可以得到APHero类型或者ADHero类型。

如果软件开发人员记忆比较好,能记得哪个是哪个,还是可以的。但是开发人员会犯错误,会记错,容易出现类型转换异常。

import java.util.ArrayList;

import charactor.ADHero;
import charactor.APHero;

public class TestGeneric {
	public static void main(String[] args) {
		ArrayList heros = new ArrayList();
		
		heros.add(new APHero());
		heros.add(new ADHero());
		
		APHero apHero = (APHero) heros.get(0);
		ADHero adHero = (ADHero) heros.get(1);
		
		// java.lang.ClassCastException: charactor.APHero cannot be cast to charactor.ADHero
//		ADHero adHero2 = (ADHero) heros.get(0); 
		
	}
}

使用泛型

使用泛型的好处:

  • 泛型的用法是在容器后面添加<Type>
  • Type可以是类,抽象类,接口
  • 泛型表示这种容器,只能存放APHero,ADHero就放不进去了。
import java.util.ArrayList;

import charactor.ADHero;
import charactor.APHero;

public class TestGeneric {
	public static void main(String[] args) {
		ArrayList<APHero> heros = new ArrayList<APHero>();
		// 只有APHero可以放进去
		heros.add(new APHero());
		
		// ADHero甚至放不进去
		// The method add(APHero) in the type ArrayList<APHero> is not applicable for the arguments (ADHero)
//		heros.add(new ADHero());
		
		// 获取的时候也不需要进行转型,因为取出来的一定是APHero
		APHero apHero = heros.get(0);
	}
}

子类对象

假设容器的泛型是Hero,那么Hero的子类 APHero,ADHero 都可以放进去。

不过,和Hero无关的类型Item还是放不进去。

import java.util.ArrayList;

import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
import property.Item;

public class TestGeneric {
	public static void main(String[] args) {
		ArrayList<Hero> heros = new ArrayList<Hero>();
		
		// 只有APHero可以放进去
		heros.add(new APHero());
		heros.add(new ADHero());
		
		// 和Hero无关的类型Item还是放不进去
		// The method add(Hero) in the type ArrayList<Hero> is not applicable for the arguments (Item)
//		heros.add(new Item());
	}
}

2. 支持泛型的类

以Stack栈为例,如果不使用泛型:

  • 当需要一个只能放Hero的栈的时候,就需要设计一个HeroStack
  • 当需要一个只能放Item的栈的时候,就需要设计一个ItemStack

HeroStack类:

import java.util.LinkedList;
 
import charactor.Hero;
   
public class HeroStack {
   
    LinkedList<Hero> heros = new LinkedList<Hero>();
       
    public void push(Hero h) {
        heros.addLast(h);
    }
   
    public Hero pull() {
        return heros.removeLast();
    }
   
    public Hero peek() {
        return heros.getLast();
    }
       
    public static void main(String[] args) {
           
        HeroStack heroStack = new HeroStack();
        for (int i = 0; i < 5; i++) {
            Hero h = new Hero("hero name " + i);
            System.out.println("压入 hero:" + h);
            heroStack.push(h);
        }
        for (int i = 0; i < 5; i++) {
            Hero h =heroStack.pull();
            System.out.println("弹出 hero" + h);
        }
    }
   
}

ItemStack类:

import java.util.LinkedList;
 
import property.Item;
   
public class ItemStack {
   
    LinkedList<Item> Items = new LinkedList<Item>();
       
    public void push(Item h) {
        Items.addLast(h);
    }
   
    public Item pull() {
        return Items.removeLast();
    }
   
    public Item peek() {
        return Items.getLast();
    }
       
    public static void main(String[] args) {
           
        ItemStack ItemStack = new ItemStack();
        for (int i = 0; i < 5; i++) {
            Item item = new Item("Item name " + i);
            System.out.println("压入 Item:" + item);
            ItemStack.push(item);
        }
        for (int i = 0; i < 5; i++) {
            Item item = ItemStack.pull();
            System.out.println("弹出 Item" + item);
        }
    }
   
}

支持泛型的Stack:

  • 设计一个支持泛型的栈MyStack
  • 设计这个类的时候,在类的声明上,加上一个<T>,表示该类型支持泛型。
  • T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但一般约定俗成使用T,代表类型。
import java.util.LinkedList;

import charactor.Hero;
import property.Item;

public class MyStack <T> {
	LinkedList<T> values = new LinkedList<T>();
	
	public void push(T t) {
		values.add(t);
	}
	
	public T pull() {
		return values.removeLast();
	}
	
	public T peek() {
		return values.getLast();
	}
	
	public static void main(String[] args) {
		// 在声明这个Stack的时候,使用泛型<Hero>就表示该Stack只能放Hero
		MyStack<Hero> heroStack = new MyStack<>();
		heroStack.push(new Hero());
		// 不能放Item
		// The method push(Hero) in the type MyStack<Hero> is not applicable for the arguments 
//		heroStack.push(new Item());
		
		// 在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
		MyStack<Item> itemStack = new MyStack<>();
		itemStack.push(new Item());
		// 不能放Hero
		// The method push(Item) in the type MyStack<Item> is not applicable for the arguments (Hero)
//		itemStack.push(new Hero());
		
	}
}

3. 通配符

? extends

ArrayList<? extends Hero>  heroList 表示这是一个Hero泛型或者其子类泛型

  • heroList 的泛型可能是Hero
  • heroList 的泛型可能是APHero
  • heroList 的泛型可能是ADHero

所以,可以确凿的是,从heroList取出来的对象那个,一定是可以转型成Hero的。

但是,不能往里面放东西,因为:

  • 放APHero就不满足<ADHero>
  • 放ADHero又不满足<APHero>

? extends

import java.util.ArrayList;

import charactor.APHero;
import charactor.Hero;
    
public class TestGeneric {
    
    public static void main(String[] args) {
        ArrayList<APHero> apHeroList = new ArrayList<APHero>();
        apHeroList.add(new APHero());
        
        ArrayList<? extends Hero> heroList = apHeroList;
        // ? extends Hero 表示这是一个Hero泛型的子类泛型
        
        // heroList 的泛型可以是Hero
        // heroList 的泛型可以是APHero
        // heroList 的泛型可以是ADHero
         
        // 可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
        Hero h = heroList.get(0);
        
        // 但是,不能往里面放东西
//        heroList.add(new ADHero()); // 编译错误,因为heroList的泛型,有可能是APHero
         
    }   
}

? super

ArrayList<? super Hero> heroList 表示这是一个Hero泛型或者其父类泛型。

  • heroList的泛型可能是Hero
  • heroList的泛型可能是Object

可以往里面插入Hero以及Hero的子类

但取出来有风险,因为不确定取出来的是Hero还是Object。

? super

import java.util.ArrayList;

import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
    
public class TestGeneric {
    
    public static void main(String[] args) {  
        ArrayList<? super Hero> heroList = new ArrayList<Object>();
        // ?super Hero 表示heroList的泛型是Hero或者其父类泛型
        
        // heroList 的泛型可以是Hero
        // heroList 的泛型可以是Object
         
        // 所以就可以插入Hero
        heroList.add(new Hero());
        // 也可以插入Hero的子类
        heroList.add(new APHero());
        heroList.add(new ADHero());
        
        // 但是,不能从里面取数据出来,因为其泛型可能是Object,而Object强转Hero会失败
//        Hero h = heroList.get(0); 
        // 但是,加强转符号会可以
        Hero h = (Hero) heroList.get(0);  
    }   
}

泛型通配符 ?

泛型通配符 ? 代表任意类型。

既然 ? 代表任意泛型,那么换句话说,这个容器什么泛型都有可能。所以只能以Object的形式取出来,并且不能往里面放对象,因为不知道到底是一个什么泛型的容器。

 

泛型通配符?

import java.util.ArrayList;

import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
    
public class TestGeneric {
    
    public static void main(String[] args) {  
        ArrayList<APHero> apHeroList = new ArrayList<APHero>();
        // ?泛型通配符,表示任意泛型
        ArrayList<?> generalList = apHeroList;
        
        // ?的缺陷1:既然?代表任意类型,那么换句话说,你就是不知道这个容器里是什么类型
        // 所以只能以Object的形式取出来 
        Object o = generalList.get(0);
        
        // ?的缺陷2:既然?代表任意泛型,那么既有可能是Hero,也有可能是Item
        // 所以,放哪种对象进去,都有风险,结果就是什么类型的对象,都不能放进去
        generalList.add(new Item()); // 编译错误 因为?代表任意泛型,很有可能不是Item
        generalList.add(new Hero()); // 编译错误 因为?代表任意泛型,很有可能不是Hero
        generalList.add(new APHero()); // 编译错误  因为?代表任意泛型,很有可能不是APHero
    }   
}

总结:

如果希望只取出,不插入,就使用 ? extends Hero

如果希望只插入,不取出,就使用 ? super Hero

如果希望,又能插入,又能取出,就不要用通配符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值