C#的泛型操作
C#的泛型是在运行期,用Reflection动态查询具体泛型的。因为.Net的具体泛型对象里面包含有类型信息。可以定义泛型数组,可以添加约束使其可以new。
泛型类型可以是引用类型,也可以是基本数据类型。
如
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Csharp泛型研究
{
class HasF
{
public void f()
{
Console.WriteLine("执行f()");
}
}
class Manipulator<T>
{
Object obj;
public Manipulator(T x)
{
obj = x;
}
public void manipulate()
{
if (obj is HasF)
{
((HasF)obj).f();
}
else
{
Console.WriteLine("not f()");
}
}
}
class Program
{
static void Main(string[] args)
{
HasF hf = new HasF();
Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
manipulator.manipulate();
Console.ReadKey();
}
}
//output:执行f()
}
Java的泛型操作
Java没有这种能力,因为具体对象里面并没有包含类型信息。Java的(属于Class)常量池中会存放一些泛型成员(属性或者方法)的泛型字符串信息,但那些都是静态信息,程序可以在运行期读取这些静态类型声明信息,但也就到此为止了。使用擦除机制的代价就是泛型不能用于显式地引用运行时类型的操作之中,例如转型、instanceof操作或new表达式。因为所有关于参数的类型信息都丢失了。
在泛型代码内部,无法获得任何有关泛型参数类型的信息。
不能定义泛型类型参数的数组如T[],不能通过new T()的方式实例化泛型,等。
Java的泛型不支持值类型(使用的话会被自动包装成引用类型)。
Java泛型的核心概念就是:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
要记住:泛型类型只有在编译期类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。
class Manipulator<T> {
private Object obj;
public Manipulator(T x) {
obj = x;
}
// Error: cannot find symbol: method f():
public void manipulate() {
if(obj.getClass().isInstance(HasF.class)){
((HasF)obj).f();
}else{
System.out.println("泛型不能用于显式地引用运行时类型的操作之中");
}
}
}
public class Manipulation {
public static void main(String[] args) {
HasF hf = new HasF();
Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
manipulator.manipulate();
}
}
//output: 泛型不能用于显式地引用运行时类型的操作之中
总结:
从使用角度来说,C#的泛型自然更加强大,因为可以真正实现动态Reflection泛型信息。对于Java的泛型,简单的讲,它的好处只在编译时,运行时没有任何泛型的意义。当你在使用已有的泛型类时,这通常能满足要求;但如果你要自己定义泛型类,那你得知道它有多少你觉得它应该可以但事实上不可以的事情。
附:C#泛型类实例化的理论
C#泛型类在编译时,先生成中间代码IL,通用类型T只是一个占位符。
在实例化类时,根据用户指定的数据类型代替T并由即时编译器(JIT)生成本地代码,这个本地代码中已经使用了实际的数据类型,等同于用实际类型写的类,所以不同的封闭类的本地代码是不一样的。
按照这个原理,我们可以这样认为:泛型类的不同的封闭类是分别不同的数据类型。
例:Stack<int>和Stack<string>是两个完全没有任何关系的类,你可以把他看成类A和类B,这个解释对泛型类的静态成员的理解有很大帮助。 class Program
{
static void Main(string[] args)
{
List<String> list1 = new List<string>();
List<int> list2 = new List<int>();
Console.WriteLine(list1.GetType().Equals(list2.GetType()));
Console.ReadKey();
}
}//output;false
附:Java泛型的理论
Java中泛型实现使用的擦除机制,为类型参数传入类型并不导致新类型出现,即传入了类型参数后在运行时仍然完全不知道类型参数的具体类型,它的目的是为了兼容非泛型(所以可以在泛型和非泛型之间隐式转换,会有编译警告但不会有编译错误,这当然其实并不安全);这同时衍生了一系列问题。
泛型是一种折中。要支持之前的非泛型代码,允许非泛型代码和泛型代码共存。为了实现这种迁移兼容性,每个类库和应用程序都必须与其他所有的部分是否使用了泛型无关。这样,它们必须不具备探测其他类库是否使用了泛型的能力。因此,某个特定的类库使用了泛型这样的证据必须被“擦除”。
import java.util.*;
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}//output:true