关于泛型的基本使用

学习集合框架相关内容之前还是要把泛型好好看下,要不各种源代码看得就很难受了,一遇到<? ><T> 这样的一些表述就头大了,这部分可结合着集合的相关内容一起了解。

泛型基本概念(Genetics)

就像圣思园视频里讲的,用一句比较好的话解释就是:变量类型的参数化。泛型基本思想与C++的模板中的思想比较类似,但是在还有一些区别的比如具体的实现方式上。

使用集合的时候比如按照下面的没有用泛型的方式(其实是<? Extends Object>):

List list=new ArrayList();

List.add(“abc”);list.add(new Integer(1));list.add(new Boolean(true))

String str=(String)list.get(0);Integer a=(Integer)list.get(1);String str2=(String)list.get(2)

上面的例子在编译的时候不会报错,在运行的时候会报错,因为index=2位置上的元素是一个Boolean类型的,这里却要强制转化成一个String类型的。

这就是之前的弊端,因为所有的类全是Object的类型的子类,因此全当做Object类型来处理,但是在取出的时候恢复到原来的类型就遇到了问题,需要进行强制的转换,这样操作虽然可以但是不能保证安全性,就像上面的例子那样。

可以说泛型的一个重要作用就是为了避免强制类型的转换,定义的时候逻辑与之前的相同,涉及到具体的类型信息的时候,用一个符号来代替(所谓类型的参数化),不变应万变。

比如下面一个简单的泛型例子:

package com.test.Genetics;

class Genetics<T>{

//这里的T 可以看做是表示类型的参数

//代码中遇到类型的部分 就用T来替代就好

//T看做是一个变量 具体传入的类型只有在运行的时候才能知道

public T foo;

public T getFoo() {

return foo;

}

public void setFoo(T foo) {

this.foo = foo;

}

}

public class testGenetics <T> {

public static void main(String[] args) {

Genetics<Boolean> foo1=new Genetics<Boolean>();

Genetics<Integer> foo2=new Genetics<Integer>();

foo1.add("abc");

}

}

可以看到上面的例子,在实际生成类的实例的时候要预先把类型信息填入其中,这样要是add了其他的类型的实例进去就会报错了,foo1.add(“abc”)这句在编译的时候就会报错,而不会等到运行的时候。实际的集合框架都是通过泛型来实现的,这样在声明的时候就要指定好类型信息,在取出的时候也不用再进行强制的类型转换了。

这个是传入两个泛型参数的例子:

package com.test.Genetics;

//这个类里面有两个通过泛型表示的类型变量

public class testGeneticb <T1,T2> {

private T1 foo1;

private T2 foo2;

public T1 getFoo1() {

return foo1;

}

public void setFoo1(T1 foo1) {

this.foo1 = foo1;

}

public T2 getFoo2() {

return foo2;

}

public void setFoo2(T2 foo2) {

this.foo2 = foo2;

}

public static void main(String[] args) {

testGeneticb<Integer,Boolean> foo=new testGeneticb<Integer,Boolean>();

foo.setFoo1(10);

foo.setFoo2(new Boolean(false));

System.out.println("foo1 is "+foo.getFoo1() +" foo2 is " + foo.getFoo2());

}

}

下面这个是用泛型实现的一个简单集合,功能比较简单,主要是为了演示对于泛型的操作,注意声明泛型类的构造方法的时候不用加上<T>:

package com.test.Genetics;

public class simpleCollection <T>{

private T[] objArr;

private int index=0;

public T[] getObjArr() {

return objArr;

}

public void setObjArr(T[] objArr) {

this.objArr = objArr;

}

public int getIndex() {

return index;

}

public void setIndex(int index) {

this.index = index;

}

//默认参数的时候 数组空间大小为10

public simpleCollection(){

this.objArr=(T[])new Object[10];

}

//泛型类的构造方法不用加<>标记了

public simpleCollection(int capacity)

{

//注意无法直接创建泛型数组 要先创建Object类型的数组之后再强制转换过来

this.objArr=(T[])new Object[capacity];

}

//这里形参是一个T类型的引用 t

public void add(T t){

this.objArr[index++]=t;

}

public int getLength(){

return this.index;

}

public T get(int i){

return this.objArr[i];

}

public static void main(String[] args) {

int i;

simpleCollection<Integer> c=new simpleCollection<Integer>();

//存入元素再打印出来

for(i=0;i<5;i++){

c.add(i);

}

System.out.println("the length is "+c.getLength());

for(i=0;i<5;i++){

Integer in=c.get(i);

System.out.println(in);

}

}

}

下面这个例子用于演示泛型的嵌套

package com.test.Genetics;

class tmpGenetics<T>{

private T foo;

public T getFoo() {

return foo;

}

public void setFoo(T foo) {

this.foo = foo;

}

}

public class wrapperFoo <T>{

private tmpGenetics<T> tmpfoo;

public tmpGenetics<T> getTmpfoo() {

return tmpfoo;

}

public void setTmpfoo(tmpGenetics<T> tmpfoo) {

this.tmpfoo = tmpfoo;

}

public static void main(String[] args) {

tmpGenetics<Integer>foo=new tmpGenetics<Integer>();

foo.setFoo(10);

wrapperFoo<Integer>wrapper=new wrapperFoo<Integer>();

wrapper.setTmpfoo(foo);

//将之前存进去的foo对象拿出来 赋给一个新的tmpGenetics<Integer>

tmpGenetics<Integer>outfoo=wrapper.getTmpfoo();

System.out.println(outfoo.getFoo());

}

}

下面这个主要演示Map框架中泛型的基本使用以及迭代器的时候泛型的使用(这个可以结合集合框架的那一部分对于Map中的entry的介绍具体来看)

package com.test.Genetics;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

public class testMap {

public static void main(String[] args) {

Map<String,String> map=new HashMap<String,String>();

map.put("a", "aa");

map.put("b", "bb");

map.put("c", "cc");

map.put("d", "dd");

System.out.println("using first kind iterator way");

Set<String>keyset=map.keySet();

for(Iterator<String>iter=keyset.iterator();iter.hasNext();){

String key=iter.next();

String value=map.get(key);

System.out.println("the key is "+key+"the value is "+value);

}

//采用第二种方式迭代输出

//注意这里的set声明的写法 entry也是一个泛型类(是Map集合中的静态内部类) 其中包含了key value 

System.out.println("using second kind iterator way");

Set<Entry<String, String>>entryset=map.entrySet();

for(Iterator<Map.Entry<String, String>>iter=entryset.iterator();iter.hasNext();){

Map.Entry<String, String>entry=iter.next();

String key=entry.getKey();

String value=entry.getValue();

System.out.println("the key is "+key+"the value is "+value);

}

}

}

关于限制泛型的作用类型

在实际的开发中要是对泛型的具体实现有限制怎么处理?

比如像下面这种类型声明:

public class Genetics<T extends someclass>这种是固定的语法,其中somclass这个地方表示希望限制的类的名字,即使泛型类在实际声明的时候,<>中只能是这个类及其子类的类型名称。比如<T extends List> 那么生成实例的时候HashMap就不能往<>中放了。

实际上直接声明的<T>相当于<T extends Object>因此在默认的情况下,任何类型都可以作为类型参数,在声明一个泛型类的实例的时候放在<>中。

还有一种情况比如希望声明一个泛型类的引用foo可以实现以下方式:

foo = new Genetics<ArrayList>;

foo = new Genetics<LinkedList>;

即是说foo这个引用不是那种一一对应的关系,声明一个泛型类的引用之后可以将这个引用指向多个泛型类的实例,这就需要用到通配符的方式即

public class Genetics<? extends List> { …}

这样生成引用 Genetics<? extends List> foo之后,foo可以指向类型参数为List或其子类的不同类型的实例。还可以通过<? extends List> 这种方式声明表示继承结构在List上面的,实际中用到的比较少。

注意两种限制方式的区别:

第一种在声明泛型引用的时候就定死了,是一一对应的关系,某种特定类型的引用只能指向对应的特定类型的泛型实例。

第二种是声明引用的时候没有定义好,或者是局部限定范围,是一对多的关系,某种引用可指向限制范围内的多个特定类型的泛型实例。第二种方式仅仅是在声明一个引用的时候才用到的,在声明泛型类的时候还是用之前的方式。

使用通配符的方式要注意的一点是,这种方式意味着只能通过这个引用来取得或者是移除(设为null)实例中的某些信息,但不能增加修改信息,因为只知道这个引用指向的是somclass的子类,但是具体指向的是哪种类型在编译期间并不知道,因此编译器不能让修改操作发生,若是可以发生,就是在编译之前已经知道了具体指向的是哪个类,这个就和泛型的初衷相违背了,比如下面这个例子:

package com.test.Genetics;

class tmpGeneticsb<T>{

private T str;

public T getStr() {

return str;

}

public void setStr(T str) {

this.str = str;

}

}

public class testExtends {

public static void main(String[] args) {

tmpGeneticsb<String>ge=new tmpGeneticsb<String>();

ge.setStr("abc");

//注意采用通配符的方式 仅仅是在声明指向泛型类的引用的时候才用到的

//声明一个泛型类的时候并不能这样使用

//<? extends Object>表示了这个引用ge2可以指向所有类型的泛型实例

tmpGeneticsb<? extends Object>ge2;

ge2=ge;

//下面的操作就会无法通过编译

//ge2.setStr("cde");

}

}
时间: 2024-12-27 16:05:02

关于泛型的基本使用的相关文章

.NET编程01(泛型)

一:Object 类型:一切类型的父类,通过继承,子类拥有父类一切属性和行为:任何父类出现的地方,都可以用子类来代替: 用一个方法来完成多个方法做的事 /// <summary>    /// 普通方法类    /// </summary>    public class CommonMethod    {        /// <summary>        /// 打印个int值        /// </summary>        /// <

c#系统泛型委托

Action<T> 无返回值的系统泛型委托 namespace ConsoleApp1 { public class UserInfo { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } class Program { private static List<UserInfo> getInit() { return new List<User

泛型2

万用字符(wildcard) 以动物Animal类为例,怎样才能创建出一种ArrayList<?>里面既可以保存ArrayList<Dog>,又可以保存ArrayList<Cat>? public void takeAnimals(ArrayList<? extends Animal> animals){  //泛型中extends同时代表继承和实现. for(Animal a : animals){ a.eat(); } } 我们可以这样调用该函数: Ar

通过反射了解集合泛型的本质

通过反射了解集合泛型的本质 import java.lang.reflect.Method; import java.util.ArrayList; /** * 通过反射了解集合泛型的本质 * @author shm * */ public class MethodDemo02 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("hello"); list.add(

构造方法中使用泛型

------------siwuxie095 构造方法中使用泛型: 构造方法可以为类中的属性初始化,如果类中的属性通过泛型指定,而又需要 通过构造方法设置属性内容的时候,构造方法的定义与之前并无不同,不需要 像声明类那样指定泛型 package com.siwuxie095.generic; class Context<T>{ private T value; public Context(T value) { this.value=value; } public T getValue() {

泛型委托当参数传递

假如有一个Person类: public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string Title { get; set; } } 执行一个方法: /// <summary> /// 传递一个泛型委托方法 /// </summary> /// <param name="acti

类库,委托,is和as运算符,泛型集合

类库:其实就是一堆类文件,只不过用户看不到这些类的源代码,保密性好. 优点:保密性好缺点:如果这个方法不好用,使用者无法自己去更改它. 类文件是.cs    类库是.dll 新建项目为类库,在debug文件夹下找到dll文件 委托:委托可以理解为:函数的指针 关键词:delegate 声明委托类型:public delegate int FirstDel(int a, int b); FirstDel不是类,是委托变量,不能实例化(不能new), 创建委托变量:FirstDel 名字 = 与这个

泛型委托

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 泛型委托 { public delegate int DelCompare<T>(T t1, T t2); // public delegate int DelCompare(object o1, object o2); class Progra

java 16-8 泛型高级之通配符

泛型高级(通配符) ?:任意类型,如果没有明确,那么就是Object以及任意的Java类了 ? extends E:向下限定,E及其子类 ? super E:向上限定,E极其父类 1 import java.util.ArrayList; 2 import java.util.Collection; 3 public class GenericDemo { 4 public static void main(String[] args) { 5 // 泛型如果明确的写的时候,前后必须一致 6 C

蓝鸥Unity开发基础二——课时21 泛型

本节课我们来学习C#中的泛型,泛型是一个特殊的类型,它可以最大限度的重用我们的代码! 推荐视频讲师博客:http://11165165.blog.51cto.com/ 使用泛型能够最大限度的重用代码,保护类型安全,提高性能 泛型成员因为类型的不确定性,不能使用算术运算符,比较运算符 类型参数可以有多个,可以是编译器能够识别的任何类型 类型参数的名字不能够随便起,不能重名 一.数组类Array using System; namespace Lesson_21{    //数组类Array