java中级-3-Collection集合类知识串讲(2)-泛型及其应用规则

时间:2022-11-04 16:59:01

------- android培训java培训、期待与您交流! ----------


泛型

       泛型,JDK1.5版本以后提供的功能,其目的是为了解决安全问题。JDK1.5版本以后,出现了类型安全机制。泛型的使用,例如:<String>

ArrayList<String>a1 = new ArrayList<String> ();

       第一点,它是将运行时期抛出ClassCastException异常问题,转移到了编译时期,使得问题更容易发现并解决。

       第二点,避免了强制转换的麻烦,即类型限定(客观)为对应泛型所取类型。

       但是需要注意的一点是,equals方法是没用泛型的,因为这个方法是继承自父类Object而来,而Object类没有使用泛型。

       泛型的使用,通常常见于集合类型的使用当中。<>是用于接受类型,我们把集合中所要作为元素的数据类型传入其中即可。这其实相当于,在原有基础上直接在类对象上加了一个“类型限定”的条件。

       接下来,我们来讨论一下,是否能够在自己建的类中使用泛型这个概念。

       早起没有泛型时,我们使用的是Object多态方法来实现对应方法的广泛适用性。这种方式需要强制转换,如果不够明确导致传参错误,则会在运行时出现ClassCastException类型转换异常,而不会在编译时期体现出来,因此不够安全。而在泛型出现后,我们保留了原有优点的同时,避免了强制转换,同时增强了安全性。由此可以总结,当类中要使用的引用数据的类型不确定时,早期我们通过Object来实现兼容性,泛型出现后,我们使用泛型来实现扩展。 

/*没有泛型前的老式用法
*/
Class MyTool
{
private Object obj;
public void setObj (Object obj)
{
this.obj = obj;
}
public Object getObj ()
{
return obj;
}
}

class Test1
{
public static void main(String[] args)
{
MyTool t = new MyTool();
t.setObj (new Integer());
Integer i = (Integer) t.getObj();
}
}

/*有泛型后,自定义类的泛型使用
这种类,我们称之为泛型类
*/
Class MyTool2<M>
{
private M obj;
public void setObj (M obj)
{
this.obj = obj
}
public M getObj ()
{
return obj;
}
}

class Test2
{
public static void main(String[] args)
{
MyTool<Integer> t = newMyTool<Integer>();
t.setObj (new Integer());
Integer i = t.getObj();
}
}

       我们可以看出,泛型类的使用也是存在局限的。当一个泛型类在创建对象的时候指定了对应的数据类型,那么该对象的数据类型从此便被限定了,任何非该类型赋值,都将变成非法赋值。这样就使得,对应的方法在此时刻有了数据上的局限性。这就是只是单纯的将泛型使用在类上,而不对其中的方法定义泛型的缺点了。如果,我们需要让方法可操纵不同类型数据,那么,我们就可以将泛型的概念用在对应的方法上。如下例:

class Demo
{
public <A> A getExecute (A a)
{
System.out.println(a);
}
public <B> void getPrint (B b)
{
System.out println(b);
}
}
由此,我们可以有下面这种混合使用方式:
class Demo <A>
{
public A getExecute (A a)
{
System.out.println(a);
}
public <B> void getPrint (B b)
{
System.out println(b);
}
}

class
{
public static void main(String[] args)
{
Demo d = newDemo<Integer>();
d.getExecute(5);

//下面是内部泛型方法的调用
d.getPrint("输出一个字符串");
d.getPrint(4);
d.getPrint('n');
/*可以看出,内部泛型是具有局部有效性的,在对应方法中有效
我们可以类比局部变量与全局变量的特点,来看局部泛型与
整体泛型
*/
}
}

       需要注意的是,对于静态方法,我们是无法使用全局泛型的,此时可以将全局泛型变量视为非静态变量来考虑。如果要对非静态方法使用泛型的话,我们就得在对应的非静态方法上进行泛型声明,这样才可以使非静态方法能够使用泛型定义。如下:

public static<M> void staticFunction (M b)

//注意书写格式,不能这样书写:

public <M>static void staticFunction (M b) //这样写是错的

       同样的,我们也有接口的泛型,泛型的继承,实际的例子如下所示: 

interface myInter<X>
{
void display(X x)
}
classmyTrueTnter<X> implements myInter<X>
{
public void display (X x)
{
System.out.println("就是这样"+x);
}
}

       

       泛型的高级使用,通配符< ? >:表示类型不明确,当然,这个也可以由具体泛型代替。这两者的区别就是,具体的泛型能够被方法接受和操作,而通配符只是一个标识,无法达成这一点。例如:

       public static <P> void myFunction(Pp)
{
P this.p = p
System.out.println(this.p);
}
public static void myFunction2(<?>p)
{
System.out.println(p);
}

       但是,如果使用通配符< ? >的话,我们就会发现,在通配符使用的情况下,对应的通配符所指参数,是无法调用参数所属类的具体特有方法的。这点可以参照多态情况下,虽然提高了兼容性,但是造成了无法使用向下转型调用子类特有方法这样的情况。

       而且,多态无法向下方这样使用:

class Student extendsPerson { }
ArrayList<Person>a = new ArrayList<Student>(); //这样是错误的

       有时我们使用的时候并不需要兼容所有类型,那么这时候,我们就有对应方法,泛型限定。泛型限定用法,可以使得我们将对应的泛型代表类型做限定,这就使得我们在具有泛型兼容特性的时候,同时能够对我们想要取得的类型做一个限制,来保证有限的兼容性。

       泛型限定分为两种方式,向上限定和向下限定:

       <? extends E>:可以接受E类型,或者E的子类型,这就是向上限定;

       <? super Person>:可以接受E类型,或者E的父类型,这就是向下限定。

       向上限定的使用就如Collection类中方法addAll(Collection<? extends E> c),这就是一例典型的向上限定,限定类上限,保证添加的时候,只能添加对应规定构建Collection类对象泛型E类型和它的子类。

       向下限定的使用就如TreeSet类中方法TreeSet(Comparator<? super E> comparator),这就是一列典型的向下限定,限定了下限,这样保证了在使用比较器的时候,子类类型可以使用父类的比较器规则,而父类不能使用子类比较器的比较规则。如下例:

class Personimplements Comparable<Person>
{
public int compareTo(Person p)
{
return 1;
}
}
class Student extendsPerson { }
class myComp extendsComparator<Person>
{
public int compare(Person p1,Person p2)
{
returnp1.getName().compareTo(p2.getName());
}
}

主函数中部分:

TreeSet<Student>test_1 = new TreeSet<Student>(new myComp());

       这就是一例,子类使用父类比较器规则建立比较器对象的例子

       总之,限定主要是用于扩展:

       向上限定,使用于子类调用所处环境能够包含它的父类的情况;

       向下限定,使用于父类规则能够规范子类的情况。

       所以,向上限定常用语添加,向下限定常用语比较

       泛型的使用是为了保持一致性,简化方法定义,增强系统安全性。在已经定义了泛型的类和方法上,我们使用时并不一定需要声明泛型类型。因此,我们在通常使用的时候,可以根据具体情况来决定是否在已经定义了泛型的函数或类上,加上具体的泛型限定。但是,这样做的重要原则是,必须保证程序还是安全的,即,前面涉及宽泛定义的部分,最好还是对使用的数据类型进行一下声明。这最好的是例,就是Collection子类的定义后,使用Collections扩展方法的情况。

 


------- android培训java培训、期待与您交流! ----------