• 8-1、泛型


    1、背景

    为什么使用泛型?

    • 1、数据类型转换
    List myIntList = new ArrayList();// 1
    myIntList.add(new Integer(0));// 2
    Integer x = (Integer) myIntList.iterator().next();// 3 

    第3行 类型转换,编译器只能保证 iterator 返回的是 Object 类型。为了保证对 Integer 类型变量赋值的类型安全,必须进行类型转换。

    • 2、使用泛型解决数据类型转换
    List<Integer> myIntList = new ArrayList<Integer>(); // 1
    myIntList.add(new Integer(0)); // 2
    Integer x = myIntList.iterator().next(); // 3

    List 是一个带一个类型参数的泛型接口。List类型,这告诉我们无论何时何地使用 myIntList 变量,编译器保证其中的元素的正确的类型(必须和类型参数相同)。


    2、定义简单的泛型

    • 1、List 接口
    public interface List<E> {
    	void add(E x);
    	Iterator<E> iterator();
    }

    代表 类型参数,如同成员方法中的形参。


    3、泛型和子类继承之间的关系

    注:如果 Foo 是 Bar 的一个子类型(子类或者子接口),而 G 是某种
    泛型声明,那么 G <Foo>G<Bar>的子类型并不成立。

    即使用泛型的对象,操作时,必须保证类型参数的一致性。

    List<String> ls = new ArrayList<String>(); //1
    List<Object> lo = ls; //2 该行 编译会报错
    List<String> ls = new ArrayList<String>(); //1
    List<Object> lo = ls;//如果该行成立
    lo.add(new Object()); // 3
    String s = ls.get(0); // 4: 试图把 Object 赋值给 String
    
    /*
    这里,我们使用 lo 指向 ls。我们通过 lo 来访问 ls,一个 String 的 list。
    我们可以插入任意对象进去。结果是 ls 中保存的不再是 String。当我们试
    图从中取出元素的时候,会得到意外的结果。
    */
    

    4、泛型-通配符

    • 1、为什么使用通配符?
    //打印集合中所有元素-不使用泛型
    void printCollection(Collection c) {
    	Iterator i = c.iterator();
    	for (int k = 0; k < c.size(); k++) {
    		System.out.println(i.next());
    	}
    }
    
    //打印集合中所有元素-使用泛型
    void printCollection(Collection<Object> c) {
    	for (Object e : c) {
    		System.out.println(e);
    	}
    }
    /*
    因为类型参数需保持一致性的原则。不使用泛型前,集合内可存放各种类型的元素;使用泛型后,只能存放特定的元素;所以出现了通配符。
    */
    
    //打印集合中所有元素-使用泛型+通配符
    void printCollection(Collection<?> c) {
    	for (Object e : c) {
    		System.out.println(e);
    	}
    }
    /*
    该方法是安全的,使用通配符后,不管集合中存放什么类型的元素,都是Object 类型的(根类)。另一点使用了通配符后,对该c 对象的写入是非法的。原因如下:见下方。这是得到灵活性所付出的代价。
    */
    
    Collection<?> c = new ArrayList<String>();
    c.add(new Object());//编译不正确
    /*
    因为我们不知道 c 的元素类型,我们不能向其中添加对象。 
    另一方面,我们可以调用 get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个 Object 。
    */
    
    • 2、有限制的通配符

    图像的抽象父类

    /**
     * 图形的抽象父类-抽象类
     * @Description: TODO:
     * @author pengguozhen
     * @date 2018年2月24日 上午11:08:13
     */
    public abstract class Shape {
    	public abstract void draw(Canvas c);
    }
    

    图形实现类

    /**
     * 图形实现类-圆形
     * @Description: TODO:
     * @author pengguozhen
     * @date 2018年2月24日 上午11:10:17
     */
    public class Circle extends Shape {
    
    	
    	private int x, y, radius;
    	
    	@Override
    	public void draw(Canvas c) {
    		// TODO Auto-generated method stub
    		System.out.println("画圆!");
    	}
    
    }
    
    
    /**
     * 矩形图形
     * @Description: TODO:
     * @author pengguozhen
     * @date 2018年2月24日 上午11:39:42
     */
    public class Rectangle extends Shape {
    
    	private int x, y, width, height;
    	
    	@Override
    	public void draw(Canvas c) {
    		// TODO Auto-generated method stub
    		System.out.println("画矩形!");
    	}
    
    }
    

    画布类

    public class Canvas {
    
    	
    	/**
    	 * 画特定的图形
    	 * @Description: TODO:
    	 * @return void   
    	 * @param s
    	 */
    	public void draw(Shape s) {
    		s.draw(this);//传入画布对象
    	}
    
    	/**
    	 * 画出所有图形
    	 * @Description: TODO:
    	 * @return void   
    	 * @param shapes
    	 */
    	public void drawAll1(List<Shape> shapes) {
    		for (Shape s : shapes) {
    			s.draw(this);
    		}
    	}
    	
    	/*
    	 * 现在,类型规则导致 drawAll()只能使用 Shape 的 list 来调用。它不能, 比如说对
    	 * List<Circle>来调用。这很不幸,因为这个方法所作的只是从这个 list 读取 shape,因此它应该也能对
    	 * List<Circle>调用。我们真正要的是这 个方法能够接受一个任意种类的 Shape:
    	 * 
    	 * List<? extends Shape>是有限制通配符的一个例子。这里?代表一个 未知的类型,就像我们前面看到的通配符一样。
    	 * 但是,在这里,我们知道这个未知的类型实际上是 Shape 的一个子类。
    	 * 我们说 Shape 是这个通配符 的上限(upper bound)。
    	 */
    	public void drawAll2(List<? extends Shape> shapes) {
    		for (Shape s : shapes) {
    			s.draw(this);
    		}
    	}
    	
    }

    5、泛型方法

    方法的声明也可以被泛型化——就是说,带有一个或者多个类型参数。

    	/**
    	 * 写一个方法,它用一个 Object 的数组和一个 collection 作为参数,
    	 *  完成把数组中所有 object 放入 collection 中的功能。
    	 * @Description: TODO:
    	 * @return void
    	 * @param a
    	 * @param c
    	 */
    static void fromArrayToCollection(Object[] a, Collection<?> c) {
    		for (Object o : a) {
    			c.add(o); // 编译期错误
    		}
    	}
    /*问题:
    现在,你应该能够学会避免初学者试图使用 Collection<Object>作为集
    合参数类型的错误了。或许你已经意识到使用 Collection<?>也不能工作。
    回忆一下,你不能把对象放进一个未知类型的集合中去。
    */
    
    解决这个问题的办法是使用 generic methods。就像类型声明,
    方法的声明也可以被泛型化——就是说,带有一个或者多个类型参数。
    
    static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    		for (T o : a) {
    			c.add(o); // correct
    		}
    	}
    	
  • 相关阅读:
    学习方式的反省
    我又辞职了!
    mysql完全备份,增量备份及恢复脚本
    marquee.js jQuery 多功能无缝滚动插件
    ul与li应用样式及其兼容性
    闲谈 JavaScript 之事件绑定(转载自万戈)
    JavaScript 中万变不离其宗的 this 应用场景
    ScrollUp – 超轻量的可定制的回到顶部 jQuery 插件
    jQuery之Tab切换代码改进
    jQuery Unveil – 另一款非常轻量的延迟加载插件
  • 原文地址:https://www.cnblogs.com/pengguozhen/p/14779562.html
Copyright © 2020-2023  润新知