软硬轨道,泛型琐碎之泛型上下限

by admin on 2019年3月26日

布署参数

  • 特色:DVHZ 移火车 录制轨道车 软硬轨道 脚轮三脚架通用引导方便
    【大Y型B型】

查看完整参数>>

1. 简介:

2.0 版 C# 语言和公共语言运维时 (CLR) 中追加了泛型。
泛型将品种参数的概念引入 .NET
Framework,类型参数使得设计如下类和措施成为大概:那一个类和措施将八个或多个品种的钦定推迟到客户端代码证明并实例化该类或格局的时候。

泛型的命名规范

为了更好地去驾驭泛型,大家也须求去领略java泛型的命名规范。<br />
为了与java关键字不一样开来,java泛型参数只是选拔3个大写字母来定义。各个常用泛型参数的意义如下:

E — Element,常用在java
Collection里,如:List<E>,Iterator<E>,Set<E>
<br />K,V — Key,Value,代表Map的键值对<br />
N — Number,数字<br />
T — Type,类型,如String,Integer等等<br />
S,U,V etc. – 2nd, 3rd, 4th 类型,和T的用法一样

怎么在泛型中,不要选取原始类型(不带项目参数的泛型)?比如要接纳List<?>,不要简单的使用List
  • 选拔不带项目参数的泛型,会废弃泛型全体的安全性和全数表现力的便宜
  • 之富有不带项目参数的泛型合法,是为着包容java5事先的旧代码
  • 在class literals中必须求用raw types,比如:List.class,
    String[].class,int.class,而不能应用List<String>.class 和
    List<?>.class。
  • 软硬轨道,泛型琐碎之泛型上下限。在instanceof操作中,要用raw types,比如:

if (o instanceof Set) {       // Raw type
    Set<?> s = (Set<?>) o;    // Wildcard type
    ...
}

2. 语法:

    class ClassName<Type>
    {
         public void Add(Type input)
        {
        }
    }

    class Program
    {
        public void test0()
        {
            ClassName<int> testClass = new ClassName<int>();
            testClass.Add(5);
            print<int>(5, 6);
            print(5, 6); // <>可以省略
        }

        public void print<T1>(T1 x, T1 y)
        {
            Console.WriteLine(x.ToString(),y.ToString());
        }

    }

上下限 概览

  public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
  }
 ? 所代表的类型必须是E的父类或就是E 

 Collection的addAll:
 addAll(Collection<? extends E> c) 
 ? 所代表的类型得是E的子类或就是E
泛型中,Set<String>,Set<Object>,Set<?>,以及Set的区别?
  • Set<String>代表set中的成分是String类型;Set<String>,Set<Object>和Set<?>
    都以Set的子类型;Set<Object>代表set中的元素是Object类型;Set<?>是1个通配符类型,代表set中的成分只好是某种未知类型的Objects;而Set是raw
    type,它不属于泛型系统。

3. 约束:

List<?>

List<?>
常常用来作为方法参数类型或艺术的回来值类型,其参数或其重回值是1个不解类型,能够毫无疑问的是,?是Object,联想一下多态,就会稍为驾驭了。

上面包车型地铁代码是会出现分外的:

    List<?> poems = new ArrayList<String>();
    poems.add("life is a song");

?表示那些List集合中保存的因素是未知类型的,Java不容许把对象放进多少个不明不白类型的汇集中,符合规律的:

package generic;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhoujie on 1/25/16.
 */
public class First {
    /**
     * ? 都处理成Object, 处理所有类型
     * 可以通过返回一个 List<?>的值, 都被当成Object来处理
     * @param c
     */
    public static void display(List<?> c) {
        for (int i = 0; i < c.size(); i++) {
            // c.get(i) 是Object类型的
            System.out.println(c.get(i));
        }
    }

    public static void main(String[] args) {
        List<String> poems = new ArrayList<>();
        poems.add("life is a song");
        poems.add("some one like you");
        poems.add("you are so beautiful");
        display(poems);
    }
}
在泛型中,相对于Array,为何事先采取List?
  • Array是协变的(covariant),即联合署名转变,比如

Sub 是 Super的子类型,Sub[] 也是 Super[]的子类型

而List是不变的(invariant),即对于八个分化的类型Type1 和
Type2,则List<Type1>既不是List<Type2>的子类型,也不是List<Type2>的超类型。

  • Array是reified(物化,
    具体的),即它确保运转期类型的不易;而List是erasure(擦除化的),即它确认保障编写翻译期类型的正确性,而在运行期时,它的要素的具体品种被甩掉(erasure是为着包容Java5事先的代码)
  • 澳门威尼斯赌场,鉴于上述两点,java 禁止创制泛型数组
1> 约束简介

在概念泛型类时,能够对客户端代码可以在实例化类时用于项目参数的品各个类施加限制。
借使客户端代码尝试采纳某些约束所区别意的档次来实例化类,则会生出编写翻译时不当。
那几个限制称为约束。 约束是利用 where上下文关键字内定的。
下表列出了六连串型的牢笼:

约束 说明
T : struct 类型参数必须是值类型。
T : class 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。
T:new() 类型参数必须具有无参数的public构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。
T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。

List<? extends Animal>, 通配符的上限

Animal.java:

public abstract class Animal {
   public abstract void sing(Perform p);
}

Cat.java:

public class Cat extends Animal {
    @Override
    public void sing(Perform p) {
        System.out.println("Hello, audience, I'm a Cat.");
    }
}

Dog.java:

public class Dog extends Animal {
    @Override
    public void sing(Perform p) {
        System.out.println("Hello, audience, I'm a dog.");
    }
}

Perform.java:

public class Perform {
    public void perform(List<Animal> animals) {
        for (Animal a : animals) {
            a.sing(this);
        }
    }

}

下边写二个类测试一下地点的多少个类:

public class TestGeric {
    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();
        Perform p = new Perform();
        dogs.add(new Dog());
        p.perform(dogs);  // 此处代码编译出现异常
    }
}

List<Dog>不可能和perform中的参数 List<Animal>
animals对应起来,表明List<Dog>并不是List<Animal>的子类型,能够将Perform类的代码改成:

public class Perform {
    public void perform(List<?> animals) {
        for (Object o : animals) {
            Animal a = (Animal)o;
            a.sing(this);
        }
    }
}

诸如此类的精益求精使用了泛型却还亟需展开强制类型转换,略显繁琐。
连续创新Perform类:

public class Perform {
    public void perform(List<? extends Animal> animals) {
        for (Animal a : animals) {
            a.sing(this);
        }
    }
}

算是引出了可喜的List<? extends
Animal>,?表示的种类能够是Animal类型和Animal的子类。能够把Animal称为那一个通配符(?)的上限(upper
bound)。
和一起来讲的List<?>一样,List<? extends Animal>
平时也是当做函数的参数来钦赐,

public void addMonkey(List<? extends Animal> animals) {
    // 下行代码出现编译错误
    animals.add(0, new Monkey());
}


?的泛型表明一般都当做方法参数或方法重回值来使用,也便是本笔记中描述的泛型方法有关。
再有正是,如下代码:

public class Apple<T extends Number> {
    T col;
    public static void main(String[] args) {
        //类型只能是Number类型或Number的子类型
        Apple<Integer> ai = new Apple<>();
        //编译错误
        //Apple<String> as = new Apple<>();
    }
}
拥抱泛型
  • Object-based 应该重构成generic types
2> 约束简介案例
    class TestClass<TestType> where TestType : struct
    {

    }

    static void Main(string[] args)
     {
            TestClass<int> t1 = new TestClass<int>();

            //TestClass<string> t2 = new TestClass<string>(); 错误,只能传递值类型
    }

<? super E>通配符的下限

来看一段代码:

public static <T> T copy(Collection<? super T> dest, Collection<T> src){
    T last = null;
    for (T ele : src) {
        last = ele;
        dest.add(ele);
    }
    return last; //返回一个T类型的变量
}
/*
<? super Type> 通配符 ? 表示它必须是Type本身,或是Type的父类
*/

// 栗子
List<Number> ln = new ArrayList<>();
List<Integer> li = new ArrayList<>();
li.add(2016);
// 此处可准确地知道最后一个被复制的元素是Integer类型 
// 与src集合的元素类型相同
Integer ele = copy(ln, li);

在集合框架中的
TreeSet<E>有1个构造器也应用了那种设定通配符下限的语法,

TreeSet(Comparator<? super E> c)

Comparator接口也是一个带泛型证明的接口:

public interface Comparator<T> {
    int compare(T fst, T snd);
}

看栗子:
Person类下边有一个子类 Student

class CompByName implements Comparator<Person>
{
    @Override
    public int compare(Person o1, Person o2) {
        int temp = o1.getName().compareTo(o2.getName());
        //如果姓名相等,根据年龄来排序
        return (temp == 0) ? (o1.getAge() - o2.getAge()) : temp; 

    }

}

class CompByStuName implements Comparator<Student>
{
    @Override
    public int compare(Student o1, Student o2) {
        int temp = o1.getName().compareTo(o2.getName());
        //如果姓名相等,根据年龄来排序
        return (temp == 0) ? (o1.getAge() - o2.getAge()) : temp; 
    }
}

/*
TreeSet(Comparator<? super E> comparator)
此处E类型是Person
下面的代码是编译不通过的 
看TreeSet的构造器(Comparator<? super E> comparator) 
根据下面代码的声明,E是Person类型,?表示的类型须是Person类型或Person的父类
而CompByStuName()是Comparator<Student>类型的
Student是Person的子类,根据TreeSet(Comparator<? super E> comparator)的约定所有就编译通不过啦

正确的:
TreeSet<Person> persons = new TreeSet<>(new CompByName()); 
TreeSet<Student> students = new TreeSet<>(new CompByName());
TreeSet<Student> students1 = new TreeSet<>(new CompByStuName());
*/   
TreeSet<Person> persons = new TreeSet<>(new CompByStuName());
//没有指定泛型,add()中的形参就是Object
persons.add(new Person("jiaobuchong", 22));
persons.add(new Person("mesi", 21));
persons.add(new Person("mark", 23));
什么样选取有边界通配符以充实API的面面俱圆?
  • PECS stands for producer-extends, consumer-super
  • E 替换成? extends E 或者? super E
  • 不用使用有境界通配符作为return types
  • 在二个措施签名中,假如一个项目参数E只出现贰回,则把替换到通配符?
3> 使用泛型约束的案由(优势)

如若要反省泛型列表中的某些项以显明它是还是不是管用,或许将它与别的某些项进行比较,则编写翻译器必须在早晚水准上保障它供给调用的运算符或格局将遭受客户端代码恐怕钦赐的其余类型参数的帮助。
那种保险是经过对泛型类定义应用一个或四个约束获得的。
例如,基类约束告诉编写翻译器:仅此类型的对象或之后类型派生的目的才可用作类型参数。
一旦编写翻译器有了那个有限帮助,它就可见允许在泛型类中调用该类型的法门。

可以对相同系列参数应用四个约束,并且封锁本人能够是泛型类型,如下所示:

        class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
        {
            // ...
        }

概念泛型接口

public interface World<T> {
    void show(T t);
}

完成地点的接口:

//实现方式1:
public class WorldImpl implements World<String> {
    public void show(String t) {
        System.out.println(t);
    }
}

//实现方式2:
public class WorldImpl implements World<T> {
    public void show(T t) {
        System.out.println(t);
    }
}

测试:

//实现方式1的测试
World<String> w = new WorldImpl();
w.show("hello, girl");

//实现方式2的测试
World<String> w1 = new WorldImpl<String>();
w1.show("hello, jiaobuchong");  
为啥尽量不要把泛型(generics)和波动参数(varargs)混合使用
  • 因为varargs是建立在数组上的用空想来欺骗别人,而数组和泛型有着不谐和的相互
4> 泛型约束的注意点:

因此自律类型参数,能够扩大约束类型及其继承层次结构中的全体种类所支撑的同意操作和情势调用的数码。
因而,在规划泛型类或艺术时,借使要对泛型成员执行除不难赋值之外的别的操作或用
System.Object不帮助的别的方式,您将需求对该类型参数应用约束。在利用
where T : class 约束时,防止对项目参数使用 == 和 !=
运算符,因为那几个运算符仅测试引用同一性而不测试值相等性。

释疑泛型中的type token(类型标记)
public class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<>();

    public <T> void putFavorite(Class<T> type, T instance) {
        favorites.put(Objects.requireNonNull(type), type.cast(instance));
    }

    public <T> T getFavorite(Class<T> type) {
        return type.cast(favorites.get(type));
    }

    public static void main(String[] args) {
         Favorites f = new Favorites();
         f.putFavorite(String.class, "Java");
         f.putFavorite(Integer.class, 0xcafebabe);
         f.putFavorite(Class.class, Favorites.class);
         String favoriteString = f.getFavorite(String.class);
         int favoriteInteger = f.getFavorite(Integer.class);
         Class<?> favoriteClass = f.getFavorite(Class.class);
         System.out.printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass.getName());
    }
}

5> 约束多个参数

能够对多少个参数应用约束,并对二个参数应用多少个约束,如上边包车型大巴演示所示

         class Base { }
         class Test<T, U>
            where U : struct
            where T : Base, new()
            { }
6> 未绑定的档次参数

一直不约束的品类参数(如公共类 SampleClass<T>{}中的
T)称为未绑定的类型参数。 未绑定的门类参数具有以下规则:

  • 不可能采纳 !=和 == 运算符,因为不恐怕保险具体品种参数能支撑那些运算符。

  • 能够在它们与
    System.Object之间来回转换,或将它们显式转换为其它接口类型。

  • 能够将它们与 [null] 进行相比。 将未绑定的参数与
    null进行相比较时,尽管类型参数为值类型,则该相比较将始终再次来到 false。

7> 作为约束的项目参数

将泛型类型参数作为约束使用,在拥有友好类型参数的成员函数必须将该参数约束为含有类型的体系参数时格外有用,如下示例所示:

        class List<T>
        {
            void Add<U>(List<U> items) where U : T {/*...*/}
        }

在上边的示范中,T在 Add方法的左右文中是1个项目约束,而在 List
类的光景文中是1个未绑定的档次参数。

系列参数还可在泛型类定义中作为约束。
请注意,必须在尖括号中扬言此类型参数与任何别的品类的参数:

SampleClass<T, U, V> where T : V { }
8> 为泛型方法添加约束
        void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
        {
            T temp;
            if (lhs.CompareTo(rhs) > 0)
            {
                temp = lhs;
                lhs = rhs;
                rhs = temp;
            }
        }

泛型方法能够应用过多品类参数进行重载。
例如,下列格局能够全方位坐落同1个类中:

        void DoWork() { }
        void DoWork<T>() { }
        void DoWork<T, U>() { }

4. 泛型接口:

语法:

     interface ITest1<K> 
    {

    }

三个接口可定义多少个档次参数,如下所示:

     interface ITest1<K, V> 
    {

    }

适用于类的接二连三规则一样适用于接口:

     interface ITest1<S> 
    {

    }

    interface ITest2<K> : ITest1<K>
    {

    }

泛型接口的约束:

    interface ITest1<S> where S : class
    {

    }

泛型接口的羁绊继承

     interface ITest1<S> where S : class
    {

    }

    interface ITest2<K> : ITest1<K> where K : class
    {

    }

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图