精华内容
下载资源
问答
  • java通配符

    2019-10-09 07:26:46
    找了找关于java通配符的一些资料,下面两则写的比较清晰1.java通配符 下面是正文: 固定的泛型类型系统使用起来并没有那么令人愉快。Java的设计者发明了一种巧妙(仍然是安全的)“解决方案”:通配符类型。例如:...

    找了找关于java通配符的一些资料,下面两则写的比较清晰
    1.java通配符

    下面是正文:
          固定的泛型类型系统使用起来并没有那么令人愉快。Java的设计者发明了一种巧妙(仍然是安全的)“解决方案”:通配符类型。
    例如:Pair<? extends B>,表示任何泛型Pair类型,它的类型参数是B的子类,如Pair<BSub>,但不是Pair<Date>。
    构造一个方法:
        public static void executeFun(Pair<BSub> p){
            p.getFirst().fun();
            p.getSecond().fun();
        }
    不能将Pair<B>传给这个方法,方法的功能受到了很大的限制。解决方法是:使用通配符类型。
        public static void executeFun(Pair<? extends B> p){
            ...
        }
    类型Pair<BSub>是Pair<? extends B>的子类型。
    使用通配符会通过Pair<? extends B>的引用破坏Pair<BSub>吗?答案是不能。
    Pair<BSub> bsp = new Pair<BSub>();
    Pair<? extends B> bp = bsp;//ok
    bp.setFirst(new B());//Error
    对setFirst的调用有一个类型错误。找知道其中缘由,请仔细看看类型Pair<? extends B>。它的方法如下所示:
    ? extends B getFirst();
    void setFirst(? extends B);
    这样不可能调用setFirst方法。编译器只知道它需要某个B类型的字类型,但不知道具体是什么类型。它拒绝传递任何特定的类型---毕竟,?不能用

    来匹配。
    使用getFirst就不存在这个问题:将getFirst的返回值赋给一个B的引用是完全合法的。
    这就是引入有限定的通配符的关键之处。现在已经有办法区分安全的访问器方法和不安全的更改器方法了。
     
    通配符的超类型限定
    通配符限定与类型变量限定十分相似。但是,它还有一个附加的能力,即可以指定一个超类型限定,如下所示:
    ? super BSub
    这个通配符限制为BSub的所有超类型。已有的super关键字十分准确的描述了这种关系。
    为什么要这样做?带有超类型限定的通配符的行为与前面介绍的相反。可以向方法提供参数,但不能使用返回值。例如,Pair<? super BSub>有方法
    void setFirst(? super BSub)
    ? super BSub getFirst()
    编译器不知道setFirst方法的确切类型,但是可以用任意BSub对象(或子类型)调用它,而不能用B对象调用。然而,如果调用getFirst,返回的对

    象类型不会得到保证,只能把它赋给一个Object,如果要将返回值赋给一个非Object的变量要使用强制的类型转换。直观地讲,带有超类型限定的通

    配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
     
    下面是超类型限定的另一种应用。Comparable接口本身就是一个泛型类型。它被声明如下:
    public interface Comparable<T> {
        public int compareTo(T o);
    }
    在此,类型变量指示了o参数的类型。例如,String类实现Comparable<String>,它的compareTo方法被声明为
    public int compareTo(String o)
    很好,显式的参数有一个正确的类型。在JDK1.5之前,o是一个Object,并且该方法的实现需要强制的类型转换。因为Comparable是一个泛型类型,

    也许可以把ArrayAlg类的min方法做得更好一些?可以这样声明:
        public static <T extends Comparable<T>> T min(T[] a){
            if(a == null || a.length == 0){
                return null;
            }
            T t = a[0];
            for(int i=1;i<a.length;i++){
                if(t.compareTo(a[i]) > 0){
                    t = a[i];
                }
            }
            return t;
        }
    看起来,这样写只适用T extends Comparable更彻底,并且对于许多类来讲会工作得更好。例如,如果计算一个String数组的最小值,T就是String

    类型的,而String是Comparable<String>的字类型。但是,当处理一个GregorianCalendar对象的数组时,就会出现问题。GregorianCalendar是

    Calendar的子类,并且Calendar实现了Comparable<Calendar>。因此GregorianCalendar实现的是Comparable<Calendar>,而不是

    Comparable<GregorianCalendar>。
    在这种情况下,超类型可以用来进行救助:
        public static <T extends Comparable<? super T>> T min(T[] a){ ... }
    现在compareTo(? super T)
    有可能它被声明为使用类型T的对象,也有可能使用T的超类型(例如,当T是GregorianCalendar)。无论如何,传递一个T类型的对象给compareTo方

    法都是安全的。
    对于初学者来说,<T extends Comparable<? super T>>这样的声明看起来有点吓人。但很遗憾,因为这一声明的意图在于帮助应用程序员排除调用

    参数上的不必要的限制。对泛型没有兴趣的应用程序员可能很快就学会掩盖这些声明,想当然地认为库程序员做的都是正确的。如果是一名库程序员

    ,一定要习惯于通配符,否则,就会受到用户的责备,还要在代码中随意地添加强制类型转换直至代码可以编译。
     
    无限定通配符
    还可以使用无限定的通配符,例如,Pair<?>。咋看起来好像和原始的Pair类型一样。实际上,有很大的不同。类型Pair<?>有方法如:
    ? getFirst()
    void setFirst(?)
    getFirst的返回值只能赋给一个Object。setFirst方法不能被调用,甚至不能用Object调用。Pair<?>和Pair的本质不同在于:可以用任意的Object

    对象调用原始的Pair类的setFirst()方法。
    为什么要使用这样脆弱的类型?它对于许多简单的操作非常有用。例如,下面这个方法将用来测试一个pair是否包含了指定的对象,它不需要实际的

    类型。
        public static boolean hasNulls(Pair<?> p){
            return p.getFirst() == null || p.getSecond() == null;
        }
    如果把它转化成泛型方法,可以避免使用通配符类型:
        public static<T> boolean hasNulls(Pair<T> p)
    但是,带有通配符类型的版本可读性更强。
    2.子类型和通配符、
    1 子类型和替换原理
    在java中,如果它们的关系是通过extends或implements建立的,那么这种关系就是父类型与子类型的关系。

    替换原理:能够接受父类的就一定可以接受子类型。

    注意:List<Number>和List<Integer>不存在子类型关系。但是数组的行为就有所不同:

    Integer[] 是Number[]的子类型。

    2 拥有extends的通配符
    <? extends E>表示E类型的任意子类型(包括本身),而这?就叫做通配符,也就是任意类型。

    public static void count(Collection<? extends Integer> ints, int a) {}

    3 拥有super的通配符
    <? super T>表示T类型的任意父类型(包括本身)。

     public static void count(Collection<? super Integer> ints, int a) {}

    4 Get和Put原理
    我们怎样决定使用哪种类型的通配符,用extends还是super,或两者都不。

    Get和Put原理:当你仅仅从一个结构中获取值时,你应该使用extends。如果只想向一个结构中插入值,你应该使用super。如果想即插入又获取值时

    ,你因该避免使用通配符。说白了就是放入的条件越宽越好,因为超类能够接受他的任何子类。而取值是越明确越好,所以用子类限定,同时也限制

    了插入的范围。

    null是属于任何引用类型,而Object是任何类型的超类。

    5 数组
    在java中,数组子类型是协变的,也就是说当S是T的子类型,那么S[]就是T[]的子类型。相反,List<S>并不是List<T>的子类型。但如果引入通配符

    ,即List<S>被认为是List<?  super T>的子类型(S是T的超类型)。

    6 通配符捕获
    当调用泛型方法时,类型参数可以被用于匹配用通配符表示的未知类型,这叫做通配符捕获。

        注意:通配符表示未知类型。

    7 通配符的限制
    A)实例创建:通配符不能出现在创建对象表达式的顶层,但是嵌套的可以。

    List<?> list = new ArrayList<?>(); // 编译错误

    Map<String, ? Extends Number> map = new HashMap<String, ? Extends Number>//编译错误

    List<List<?>> lists = new ArrayList<List<?>>();//合法。

         B) 泛型方法调用:如果调用一个包含明确类型参数的泛型方法,那么这个类型参数一定不能是通配符。你可以采用类型推断或显示的传递一个

    类型参数。

    List<?> list = lists.factory();

    List<?> list = lists.<Object>factory(); //Object就是明确的类型参数。

    List<?> list = lists.<?>factory();

    但是嵌套的可以:

    List<List<?>> Lists.<List<?>>factory();//合法

     C) 超类:因为创建类实例时,首先要初始化他的父类(创建实例的约束也适合超类),所以在类的声明中,如果超类或接口有类型参数,那么这些

    类型参数一定不能是通配符。

    class AnyList extends ArrayList<?>{...} //编译错误

    class AnotherList implements List<?>{...}//编译错误

    但是嵌套的可以:

    class NestedList implements ArrayList<List<?>>{...}//合法

    转载于:https://www.cnblogs.com/FromNowOn/p/3170376.html

    展开全文
  • Java通配符

    2019-03-17 22:56:35
    package com.atguigu.java2; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.junit.Test; ... * 有限制条件的通配符 * * 举例: * L...
    package com.atguigu.java2;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    import org.junit.Test;
    
    public  class GenericTest {
    	
    	/*
    	 * 有限制条件的通配符
    	 * 
    	 * 举例:
    	 * List<? extends Number> 是 List<A> 和 List<Number>的父类。其中:类A是Number的子类
    	 * 
    	 * List<? super Number> 是 List<A> 和 List<Number>的父类。其中:类A是Number的父类
    	 * 
    	 */
    	@Test
    	public void test3(){
    		
    		List<Object> list1 = null;
    		List<Number> list2 = null;
    		List<Integer> list3 = null;
    		
    		
    		List<? extends Number> list4 = null;
    		List<? super Number> list5 = null;
    		
    		
    //		list4 = list1;
    		list4 = list2;
    		list4 = list3;
    		
    		list5 = list1;
    		list5 = list2;
    //		list5 = list3;
    		
    		
    	}
    	
    	
    	/* 通配符的使用(掌握)
    	 * ?:通配符
    	 * 比如:List<?> 作为List<A>的父类。 A:任意的一个类型。
    	 * 
    	 * 涉及通配符的集合的数据的写入和读取。
    	 * 写入:不允许向使用了通配符的集合类中添加元素。特例:null.
    	 * 读取:可以从使用了通配符的集合类中读取数据。
    	 */
    	@Test
    	public void test2(){
    		List<?> list1 = null;
    		List<Object> list2 = null;
    		List<String> list3 = new ArrayList<>();
    		list3.add("AA");
    		list3.add("BB");
    		
    		list1 = list2;
    		list1 = list3;
    		list1.add(null);
    		System.out.println(list1.size());
    		method3(list2);
    		method3(list3);
    		//1.写入
    //		list1.add("AA");
    		list1.add(null);
    		list3.add("kk");
    		//2.读取
    		Object o = list1.get(0);
    		System.out.println(o);
    		System.out.println(list1);
    		list1.remove(3);
    		System.out.println(list1);
    	}
    	
    	public void method3(List<?> list){
    		
    	}
     
    	/*
    	 * 泛型在继承上的体现(掌握)
    	 * 结论1:如果类A是类B的父类,则G<A> 与 G<B> 不满足子父类关系。
    	 * 结论2:如果类A是类B的父类,则A<G> 是 B<G>的父类
    	 */
    	@Test
    	public void test1(){
    		Object obj = new String("AA");	
    		Object[] arr1 = null;
    		String[] arr2 = new String[4];
    		arr1 = arr2;	
    		method1(arr2); 
    		System.out.println("***************");
    		List<Object> list1 = new ArrayList<>();
    		List<String> list2 = new ArrayList<>();
    //		list1 = list2;//编译不通过
    		/*反证法:
    		 *list1.add(Object obj); 
    		 */
    		
    //		String s = new Date();
    		
    //		method2(list2);
    		method2(list1);
    		Collection<Object> coll = null;
    		coll = list1;//编译通过
    		//coll = list2; 报错 
    	}
    	
    	public void method2(List<Object> list){
    		
    	}
    	
    	public void method1(Object[] arr){//Object[] arr = new String[4];
    		
    	}
    }
    

     

    展开全文
  • java 通配符

    千次阅读 2018-06-22 16:52:08
     本以为这会是一篇比较基础的博客,可一旦深究的时候,才发现很多有意思的...首先本文是在建立在java泛型基础之上的,如果你对泛型并不了解,可以点击 这里。同时为了对通配符的了解更为透切,定义如下几个类。...

    引用 : http://blog.51cto.com/peiquan/1303768

        本以为这会是一篇比较基础的博客,可一旦深究的时候,才发现很多有意思的东西,也发现了很多令人迷惑的地方。通配符是一个有趣的东西,如果你掌握了,会使你的代码更为通用(健壮性更强)。首先本文是在建立在java泛型基础之上的,如果你对泛型并不了解,可以点击 这里。同时为了对通配符的了解更为透切,定义如下几个类。

    public class Animal {
    	private String name;
    
    	public Animal(String name) {
    		this.name = name;
    	}
    	
    	public void eat() {
    		System.out.println(getName() + " can eat.");
    	}
    	
    	public String getName(){
    		return name;
    	}
    }

     

    public class Cat extends Animal {
    
    	public Cat(String name) {
    		super(name);
    	}
    
    	public void jump(){
    		System.out.println(getName() + " can jump.");
    	}
    }

     

    public class Bird extends Animal {
    
    	public Bird(String name) {
    		super(name);
    	}
    
    	public void fly(){
    		System.out.println(getName() + " can fly.");
    	}
    }

     

    public class Magpie extends Bird {
    
    	public Magpie(String name) {
    		super(name);
    	}
    
    	public void sing(){
    		System.out.println(getName() + 
    				" can not only eat,but sing");
    	}
    }


        首先我们看一下无通配符的使用示例,如下:

    public class AnimalTrainer {
    	public void act(List<Animal> list) {
    		for (Animal animal : list) {
    			animal.eat();
    		}
    	}
    }

         测试代码如下:

    public class TestAnimal {
    	public static void main(String[] args) {
    		AnimalTrainer animalTrainer = new AnimalTrainer();
    		//Test 1
    		List<Animal> animalList = new ArrayList<>();
    		animalList.add(new Cat("cat1"));
    		animalList.add(new Bird("bird1"));
    		
    		animalTrainer.act(animalList);	//可以通过编译
    		
    		//Test 2
    		List<Cat> catList = new ArrayList<>();
    		catList.add(new Cat("cat2"));
    		catList.add(new Cat("cat3"));
    		
    		animalTrainer.act(catList);		//无法通过编译
    	}
    }

        如上,Test 1 的执行应该可以理解的,不过顺带提醒一下的是,因为cat1和bird1都是Animal对象,自然可以添加List<Animal>里,具体解释可参考 java泛型基础 。对于Test 2,无法通过编译是因为List<Cat>并不是List<Animal>子类,传入参数有误,也就无法通过编译了。现在尝试去修改AnimalTrainer.act()代码,让它变得更为通用的一点,即不仅仅是接受List<Animal>参数,还可以接受List<Bird>等参数。那如何更改呢??

    一、通配符的上界

        既然知道List<Cat>并不是List<Anilmal>的子类型,那就需要去寻找替他解决的办法, 是AnimalTrianer.act()方法变得更为通用(既可以接受List<Animal>类型,也可以接受List<Cat>等参数)。在java里解决办法就是使用通配符“?”,具体到AnimalTrianer,就是将方法改为act(List<? extends Animal> list),当中“?”就是通配符,而“? extends Animal”则表示通配符“?”的上界为Animal,换句话说就是,“? extends Animal”可以代表Animal或其子类,可代表不了Animal的父类(如Object),因为通配符的上界是Animal。如下,为改进之后的AnimalTrianer

    public class AnimalTrainer {
    	public void act(List<? extends Animal> list) {
    		for (Animal animal : list) {
    			animal.eat();
    		}
    	}
    }

        再来测试一下,如下,发现Test 2 可以通过编译了:

    public class TestAnimal {
    	public static void main(String[] args) {
    		AnimalTrainer animalTrainer = new AnimalTrainer();
    		//Test 1
    		List<Animal> animalList = new ArrayList<>();
    		animalList.add(new Cat("cat1"));
    		animalList.add(new Bird("bird1"));
    		
    		animalTrainer.act(animalList);	//可以通过编译
    		
    		//Test 2
    		List<Cat> catList = new ArrayList<>();
    		catList.add(new Cat("cat2"));
    		catList.add(new Cat("cat3"));
    		
    		animalTrainer.act(catList);		//也可以通过编译
    	}
    }

        经过上述分析,可以知道List<Animal>和List<Cat>都是List<? extends Animal>的子类型,类似有List<Bird>,List<Magpie>也是List<? extends Animal>的子类型。现总结如下,对于通配符的上界,有以下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))

    • G<? extends Y> 是 G<? extends X>的子类型(如List<? extends Cat> 是 List<? extends Animal>的子类型)。
    • G<X> 是 G<? extends X>的子类型(如List<Animal> 是 List<? extends Animal>的子类型)
    • G<?> 与 G<? extends Object>等同,如List<?> 与List<? extends Objext>等同。

     

        学到这里,可能会遇到一些疑惑的地方,或者说事理解不透的地方,先观察如下两段代码片段,判断一下其是否可行??

    public void testAdd(List<? extends Animal> list){
    		//....其他逻辑
    		list.add(new Animal("animal"));
    		list.add(new Bird("bird"));
    		list.add(new Cat("cat"));
    	}

     

    List<? extends Animal> list = new ArrayList<>();
    list.add(new Animal("animal"));
    list.add(new Bird("bird"));
    list.add(new Cat("cat"));

         先分析如下:因为“? extends Animal”可代表Animal或其子类(Bird,Cat),那上面的操作应该是可行的。事实上是”不行“,即无法通过编译。为什么呢??

         在解释之前,再来重新强调一下已经知道的规则:在List<Aimal> list里只能添加Animal类对象及其子类对象(如Cat和Bird对象),在List<Bird>里只能添加Bird类和其子类对象(如Magpie),可不能添加Animal对象(不是Bird的子类),类似的在List<Cat>和List<Magpie>里只能添加Cat和Bird对象(或其子类对象,不过这没有列出)。现在再回头看一下testAdd()方法,我们知道List<Animal>、List<Cat等都是List<? extends Animal>的子类型。先假设传入的参数为为List<Animal>,则第一段代码的三个“add”操作都是可行的;可如果是List<Bird>呢??则只有第二个“add”可以执行;再假设传入的是List<Tiger>(Tiger是想象出来的,可认为是Cat的子类),则三个“add”操作都不能执行。

         现在反过来说,给testAdd传入不同的参数,三个“add”操作都可能引发类型不兼容问题,而传入的参数是未知的,所以java为了保护其类型一致,禁止向List<? extends Animal>添加任意对象,不过却可以添加 null即list.add(null)是可行的。有了上面谈到的基础,再来理解第二段代码就不难了,因为List<? extends Animal>的类型“? extends Animal”无法确定,可以是Animal,Bird或者Cat等,所以为了保护其类型的一致性,也是不能往list添加任意对象的,不过却可以添加 null

        先总结如下:不能往List<? extends Animal> 添加任意对象,除了null。

        另外提醒大家注意的一点是,在List<? extends Animal> 可以是Animal类对象或Bird对象等(只是某一类对象),反过来说,在List<? extends Animal> list里的都是Animal对象,即Bird也是Animal对象,Cat也是Animal对象(用java的语言来说就是子类可以指向父类,父类却不能指向子类),那么在Animal里的所有方法都是可以调用的,如下:

    for (Animal animal : list) { animal.eat(); }

     

    二、通配符的下界

        既然有了通配符的上界,自然有着通配符的下界。可以如此定义通配符的下界 List<? super Bird>,其中”Bird“就是通配符的下界。注意:不能同时声明泛型通配符申明上界和下界。

        在谈注意细节之前,我们先看一下通配符的使用规则——对于通配符的上界,有以下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))

    • G<? super X> 是 G<? super Y>的子类型(如List<? super Animal> 是 List<? super Bird>的子类型)。
    • G<X> 是 G<? super X>的子类型(如List<Animal> 是 List<? super Animal>的子类型)

       现在再来看如下代码,判断其是否符合逻辑:

    public void testAdd(List<? super Bird> list){
    		list.add(new Bird("bird"));
    		list.add(new Magpie("magpie"));
    	}
    List<? super Bird> list = new ArrayList<>();
    list.add(new Bird("bird"));
    list.add(new Magpie("magpie"));
    list.add(new Animal("animal"));

         看第一段代码,其分析如下,因为”? super Bird”代表了Bird或其父类,而Magpie是Bird的子类,所以上诉代码不可通过编译。而事实上是”“,为什么呢?2?

         在解疑之前,再来强调一个知识点,子类可以指向父类,即Bird也是Animal对象。现在考虑传入到testAdd()的所有可能的参数,可以是List<Bird>,List<Animal>,或者List<Objext>等等,发现这些参数的类型是Bird或其父类,那我们可以这样看,把bird、magpie看成Bird对象,也可以将bird、magpie看成Animal对象,类似的可看成Object对象,最后发现这些添加到List<? supe Bird> list里的对象都是同一类对象(如本文刚开篇提到的Test 1),因此testAdd方法是符合逻辑,可以通过编译的。:

         现在再来看一下第二段代码对于,第二、三行代码的解释和上文一样,至于最后一行“list.add(new Animal("animal"))”是无法通过编译的,为什么的??为了保护类型的一致性,因为“? super Bird”可以是Animal,也可以是Object或其他Bird的父类,因无法确定其类型,也就不能往List<? super Bird>添加Bird的任意父类对象。

        既然无法确定其父类对象,那该如何遍历List<? super Bird> ? 因为Object是所有类的根类,所以可以用Object来遍历。如下,不过貌似其意义不大。

    for (Object object : list) {//...}

        那“? super BoundingType”可以应用在什么地方呢??“? super BoundingType”应用相对广泛,只不过是混合着用。下面举个简单的例子。先假设有以下两个Student和CollegeStudent,当中CollegeStudent继承Student,如下:

    public class Student implements Comparable<Student>{
    	private int id;
    
    	public Student(int id) {
    		this.id = id;
    	}
    
    	@Override
    	public int compareTo(Student o) {
    		return (id > o.id) ? 1 : ((id < o.id) ? -1 : 0);
    	}
    }

     

    public class CollegeStudent extends Student{
    	public CollegeStudent(int id) {
    		super(id);
    	}
    }


        先需要根据他们的id对他们进行排序(注意此处是对数组对象进行排序),设计方法如下,(n指数组元素的个数):

    public static <T extends Comparable<? super T>> 
    			void selectionSort(T[] a,int n)

        先理解此方法含义,首先<T extends Comparable<T>>规定了数组中对象必须实现Comparable接口,Comparable<? Super T>表示如果父类实现Comparable接口,其自身可不实现,如CollegeStudent。先假设有一个CollegeStudent的数组,如下:

    CollegeStudent[] stu = new CollegeStudent[]{
       new CollegeStudent(3),new CollegeStudent(2),
       new CollegeStudent(5),new CollegeStudent(4)};


        执行方法 selectionSort(stu,4)是完全可以通过的。可如果定义的selectionSort方法如下:

    public static <T extends Comparable<T>> 
    			void selectionSort(T[] a,int n)

        则方法selectionSort(stu,4)不能执行,因为CollegeStudent没有实现Comparable<CollegeStudent>接口。换句话就是“? super T”使selectionSort方法变得更为通用了。selectionSort完整代码的实现可参考本文的末尾。

    三、无界通配符

        知道了通配符的上界和下界,其实也等同于知道了无界通配符,不加任何修饰即可,单独一个“?”。如List<?>,“?”可以代表任意类型,“任意”也就是未知类型。无界通配符通常会用在下面两种情况:1、当方法是使用原始的Object类型作为参数时,如下:

    public static void printList(List<Object> list) {
        for (Object elem : list)
            System.out.println(elem + " ");
        System.out.println();
    }

        可以选择改为如下实现:

    public static void printList(List<?> list) {
        for (Object elem: list)
            System.out.print(elem + " ");
        System.out.println();
    }

         这样就可以兼容更多的输出,而不单纯是List<Object>,如下:

    List<Integer> li = Arrays.asList(1, 2, 3);
    List<String>  ls = Arrays.asList("one", "two", "three");
    printList(li);
    printList(ls);

     

        2、在定义的方法体的业务逻辑与泛型类型无关,如List.size,List.cleat。实际上,最常用的就是Class<?>,因为Class<T>并没有依赖于T。

        最后提醒一下的就是,List<Object>与List<?>并不等同,List<Object>是List<?>的子类。还有不能往List<?> list里添加任意对象,除了null。

     

     

    附录:selectionSort的代码实现:(如果需要实现比较好的输出,最好重写Student的toString方法)

    public class SortArray {
    	
    	//对一组数组对象运用插入排序,n指数组元素的个数
    	public static <T extends Comparable<? super T>> 
    					void selectionSort(T[] a,int n) {
    		for (int index = 0; index < n-1; index++) {
    			int indexOfSmallest = getIndexOfSmallest(a,index,n-1);
    			swap(a,index,indexOfSmallest);
    		}
    	}
    	
    	public static <T extends Comparable<? super T>> int getIndexOfSmallest(T[] a, int first, int last) {
    		T minValue = a[first]; // 假设第一个为minValue
    		int indexOfMin = first; // 取得minValue的下标
    		for (int index = first + 1; index <= last; index++) {
    			if (a[index].compareTo(minValue) < 0) {
    				minValue = a[index];
    				indexOfMin = index;
    			}
    		}
    
    		return indexOfMin;
    	}
    	
    	public static void swap(Object[] a,int first,int second) {
    		Object temp = a[first];
    		a[first] = a[second];
    		a[second] = temp;
    	}
    	
    	public static void main(String[] args) {
    		CollegeStudent[] stu = new CollegeStudent[]{
    				new CollegeStudent(3),
    				new CollegeStudent(2),
    				new CollegeStudent(5),
    				new CollegeStudent(4)};
    		selectionSort(stu, 4);
    		for (Student student : stu) {
    			System.out.println(student);
    		}
    	}
    }
    展开全文
  • Java 通配符

    2019-09-03 17:31:58
    当没有使用通配符的情况下,我们定义一个方法: 1 2 3 4 public static <E> void test(List<E> l){ E e = l.get(0); l.set(0, e); } 我们从List中 get...

    当没有使用通配符的情况下,我们定义一个方法:

     

    1

    2

    3

    4

    public static <E> void test(List<E> l){

        E e = l.get(0);

        l.set(0, e);

    }

    我们从List中 get和set都没有问题,因为这个E 它的类型是某种明确的类型。

    而当使用通配符时来描述参数时,就有些不同了。
    我们先定义一下两种通配符:
    <? extends E> 是 Upper Bound(上限) 的通配符
    <? super E> 是 Lower Bound(下限) 的通配符

    1) 当使用 Upper Bound 通配符时:

     

    1

    2

    3

    4

    public static void test(List<?> list){

        Object e = list.get(0); // get OK

        list.set(0, e);         // set 编译报错

    }

    上面代码中通配符<?><? extends Object> 的简写。(关于<?>是否和<? extends Object>完全等价,我遇到个小插曲,在结束的时候来描述)

    在eclipse里错误提示为:

    The method set(int, capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (int, Object)

    注: <capture#2-of ?> 是一个占位符,表示编译器对通配符的捕获,更多见:
    http://www.ibm.com/developerworks/cn/java/j-jtp04298.html

    set报错的原因是因为此时方法中的类型是不可具体化的(reified),你可以传递一个String,Number,Book,等任何继承自Object的类作为List的参数类型给test方法,而list要求集合中的类型必须是一致的,set的时候没有办法保证set进去的数据类型是否和list中原本的类型一致,比如你传给test方法的是 List<Book>, 那么在方法中set进去一个Object显然类型就不一致了。这也是通配符带来灵活性的同时所要付出的代价。

    结论:使用了 <? extends E> 这样的通配符,test方法的参数list变成了只能get不能set(除了null) 或者不严谨的说它变成了只读参数了, 有些类似一个生产者,提供数据。

     

    2) 当使用 Lower Bound 的通配符时:

     

    1

    2

    3

    4

    5

    6

    7

    public static void test(List<? super Number> list){

        Number n = list.get(0);             // 编译错误

        Object o = list.get(0);             // OK

        list.set(0, new Object());          // 编译错误

        list.set(0, new Long(0));           // OK

        list.set(0, new Integer(0));        // OK

    }

    这时get只能get出最宽泛的父类型,即Object。
    这时set的时候,必须是Number或Number的子类。
    原因和上面的get类似。

    结论: 使用了<? super E> 这种通配符,test方法的参数list的get受到了很大的制约,只能最宽泛的方式来获取list中的数据,相当于get只提供了数据最小级别的访问权限(想想,你可能原本是放进去了一个Book,却只能当作Object来访问)。它更多适合于set的使用场景,像是一个消费者,主要用来消费数据。

    上面便是对通配符的使用原则的说明,简单的说 PECS原则是指导我们在泛型方法中使用通配符的直接原则。参数作为生产者使用<? extends E>,作为消费者时使用<? super E>

    展开全文
  • JAVA通配符

    千次阅读 2008-06-06 12:07:00
    上限通配符 我们想要的是一个确切元素类型未知的列表,这一点与数组是不同的。 List是一个列表,其元素类型是具体类型Number。 List是一个确切元素类型未知的列表。它是Number或其子类型。 上限 如果我们更新初始...
  • java 通配符使用示例

    2010-06-21 16:07:45
    java 通配符使用示例 java 通配符使用示例 java 通配符使用示例
  • java 通配符解惑

    2016-03-31 10:47:54
    java 通配符解惑
  • java 通配符 问题

    千次阅读 2011-01-13 11:25:00
    java 通配符问题,困惑了好久的一个问题,觉得这篇文章将解的最清楚明白:List fruit = new List() ;这是因为List是持有Fruit或其导出类的容器,而List是持有Apple的容器,它们之间并没有类型上的相关性。所以如果真...
  • Java 通配符解惑

    2016-09-27 14:39:48
    一、通配符的上界 既然知道List并不是List的子类型,那就需要去...在java里解决办法就是使用通配符“?”,具体到AnimalTrianer,就是将方法改为act(List list),当中“?”就是通配符,而“? extends Animal”则表示
  • Technorati 标记: java,排序算法,通配符 这几天无聊,又重新学起java的排序算法,为DualPivotQuickSort做准备。为了更好地适应各种情况,我们选择使用通用类型T和通配符的上下界来实现,同时这次谈的是对数组对象的...
  • java通配符的详解

    2021-04-15 10:55:09
    java中,?代表通配符通配符用法 1. 在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义 2. <? extends Object>代表上边界限定通配符 3. <? super Object>代表下边...
  • java通配符写法

    2019-05-05 20:45:00
    有时候我们会遇到这样的需求,需要把一个报文里的某些参数项通过通配符的形式配置成我们需要的结果值插入回报文中。  String filetext = "<cn>#用户身份ID(主账号)#</cn><sn>#用户姓名#</...
  • java通配符和List

    2020-05-24 20:18:02
    java泛型的使用过程中常常需要使用通配符,最常用的就是使用<?>这个无限定通配符。很多时候使用泛型时都不依赖于类型参数T中的方法,但如果使用object类的话又会受到很多限制,这时候就会用到<?> 比如...
  • (1)JAVA通配符T、E、K、V的区别 这些字母都属于泛型的通配符,其实就是一个名称区别,习惯,是一种规范。 ?:不确定的JAVA类型 T:一个具体的JAVA类型K (key)V(value):键值对Key,ValueE: 代表...
  • Java 通配符泛型例子

    2019-07-05 18:28:30
    总体来说,泛型通配符就是为了支持多态时父子类,接口扩展类之间的相互转换而生 package test; import java.util.ArrayList; import java.util.List; public class GenericTester { public static void mai.....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,247
精华内容 2,098
关键字:

java通配符

java 订阅