MrB*_*les 80 c# generics types
我以为我会把这个垒球提供给任何想要从公园里击出的人.什么是泛型,泛型的优点是什么,为什么,在哪里,我应该如何使用它们?请保持相当基本.谢谢.
ljs*_*ljs 121
Jam*_*hek 47
我真的不想重复自己.我讨厌经常输入相同的东西.我不喜欢多次重复一些事情而略有不同.
而不是创建:
class MyObjectList {
MyObject get(int index) {...}
}
class MyOtherObjectList {
MyOtherObject get(int index) {...}
}
class AnotherObjectList {
AnotherObject get(int index) {...}
}
Run Code Online (Sandbox Code Playgroud)
我可以构建一个可重用的类...(在你不想因为某些原因使用原始集合的情况下)
class MyList<T> {
T get(int index) { ... }
}
Run Code Online (Sandbox Code Playgroud)
我现在效率提高了3倍,我只需保留一份.为什么你不想保持更少的代码?
对于非集合类(例如a Callable<T>或Reference<T>必须与其他类交互)也是如此.你真的想扩展Callable<T>和 Future<T>其他所有相关的类来创建类型安全的版本吗?
我不.
coo*_*ird 20
不需要进行类型转换是Java泛型的最大优势之一,因为它将在编译时执行类型检查.这将减少ClassCastException可以在运行时抛出的s 的可能性,并且可以导致更强大的代码.
但我怀疑你完全清楚这一点.
我每次看泛色都会让我头疼.我发现Java最好的部分是它的简单性和最小的语法和泛型并不简单,并添加了大量的新语法.
起初,我也没有看到仿制药的好处.我开始从1.4语法学习Java(尽管当时Java 5已经出来了),当我遇到泛型时,我觉得编写的代码更多,我真的不明白它的好处.
现代IDE使得使用泛型编写代码变得更容易.
大多数现代的,不错的IDE都足够聪明,可以协助使用泛型编写代码,尤其是代码完成时.
这是一个Map<String, Integer>用a 做的例子HashMap.我必须输入的代码是:
Map<String, Integer> m = new HashMap<String, Integer>();
Run Code Online (Sandbox Code Playgroud)
事实上,只需要打造一个新的东西就可以了HashMap.但是,实际上,在Eclipse知道我需要的东西之前,我只需要输入这么多内容:
Map<String, Integer> m = new Ha Ctrl+Space
没错,我确实需要HashMap从候选列表中进行选择,但基本上IDE知道要添加什么,包括泛型类型.使用正确的工具,使用泛型也不算太糟糕.
此外,由于类型是已知的,因此从通用集合中检索元素时,IDE将表现为该对象已经是其声明类型的对象 - 不需要为IDE转换以了解对象的类型是.
泛型的一个关键优势来自于它与Java 5新功能的良好配合.这是一个将整数投入a Set并计算其总数的示例:
Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);
int total = 0;
for (int i : set) {
total += i;
}
Run Code Online (Sandbox Code Playgroud)
在那段代码中,有三个新的Java 5特性:
首先,基元的泛型和自动装箱允许以下行:
set.add(10);
set.add(42);
Run Code Online (Sandbox Code Playgroud)
整数10被自动装箱到一个Integer值为10.(同样如此42).然后将Integer其扔进Set已知Integer的东西中.试图抛出一个String会导致编译错误.
接下来,对于for-each循环,需要以下三个:
for (int i : set) {
total += i;
}
Run Code Online (Sandbox Code Playgroud)
首先,Set包含Integers用于for-each循环.每个元素都声明为a,int并且在Integer取消装回原始元素时允许int.并且这种拆箱的事实是众所周知的,因为泛型被用来指明在那里存在Integers Set.
泛型可以是将Java 5中引入的新功能集成在一起的粘合剂,它只是使编码更简单,更安全.而且大多数时候IDE都足够聪明,可以为你提供好的建议,所以一般情况下,输入的内容不会太多.
坦率地说,从Set示例中可以看出,我觉得利用Java 5功能可以使代码更简洁,更健壮.
编辑 - 没有泛型的示例
以下是Set不使用泛型的上述示例的说明.这是可能的,但不是很愉快:
Set set = new HashSet();
set.add(10);
set.add(42);
int total = 0;
for (Object o : set) {
total += (Integer)o;
}
Run Code Online (Sandbox Code Playgroud)
(注意:上面的代码将在编译时生成未经检查的转换警告.)
使用非泛型集合时,输入集合的类型是类型的对象Object.因此,在这个例子中,a Object是被add编入集合的内容.
set.add(10);
set.add(42);
Run Code Online (Sandbox Code Playgroud)
另外,在上述线,自动装箱在起作用-原始int值10和42正在autoboxed到Integer对象,它们被添加到Set.但是,请记住,Integer对象是作为Objects 处理的,因为没有类型信息可以帮助编译器知道Set应该期望的类型.
for (Object o : set) {
Run Code Online (Sandbox Code Playgroud)
这是至关重要的部分.for-each循环工作的原因是因为Set实现了Iterable接口,该接口返回Iterator带有类型信息(如果存在).(Iterator<T>是的.)
但是,由于没有类型信息,Set将返回一个Iterator将返回Setas中Object的值,这就是为什么在for-each循环中检索的元素必须是类型的原因Object.
现在Object从中检索Set,需要Integer手动转换为执行添加:
total += (Integer)o;
Run Code Online (Sandbox Code Playgroud)
这里,从a Object到a执行类型转换Integer.在这种情况下,我们知道这将始终有效,但手动类型转换总是让我觉得它是脆弱的代码,如果在其他地方进行微小的更改可能会被损坏.(我觉得每个类型都在ClassCastException等待发生,但我离题了...)
在Integer现在拆箱成int并允许执行加法入int变量total.
我希望我可以说明Java 5的新功能可以与非泛型代码一起使用,但它并不像使用泛型编写代码那样干净和直接.而且,在我看来,为了充分利用Java 5中的新功能,人们应该研究泛型,如果至少允许编译时检查以防止无效的类型转换在运行时抛出异常.
Tom*_*ine 15
如果你要搜索的Java bug数据库1.5发布之前,你会发现七倍的错误与NullPointerException比ClassCastException.所以它似乎不是一个很好的功能,找到错误,或至少在一些烟雾测试后仍然存在的错误.
对我来说,泛型的巨大优势在于它们在代码中记录了重要的类型信息.如果我不想在代码中记录该类型信息,那么我将使用动态类型语言,或者至少使用具有更隐式类型推断的语言.
保持对象的集合本身并不是一种糟糕的风格(但是通常的风格是有效地忽略封装).这取决于你在做什么.将集合传递给"算法"稍微容易用泛型检查(在编译时或编译时).
Apo*_*isp 10
Java中的泛型有助于参数多态性.通过类型参数,您可以将参数传递给类型.就像一个方法,比如String foo(String s)模拟某些行为,不仅仅是针对特定的字符串,而是针对任何字符串s,所以类似于List<T>某种行为的类型,不仅针对特定类型,而且适用于任何类型.List<T>说对于任何类型T,都有一种List元素是Ts.所以List实际上是一个类型构造函数.它将一个类型作为参数,并构造另一个类型作为结果.
以下是我每天使用的泛型类型的几个示例.首先,一个非常有用的通用接口:
public interface F<A, B> {
public B f(A a);
}
Run Code Online (Sandbox Code Playgroud)
这个接口表示对于某些两种类型,A并且B,有一个函数(被调用f)接受A并返回一个B.当你实现这个接口,A并且B可以是任何你想要的类型,只要你提供一个函数f,是以前者,并返回后者.这是接口的示例实现:
F<Integer, String> intToString = new F<Integer, String>() {
public String f(int i) {
return String.valueOf(i);
}
}
Run Code Online (Sandbox Code Playgroud)
在泛型之前,通过使用关键字进行子类化来实现多态性extends.使用泛型,我们实际上可以取消子类化并使用参数多态.例如,考虑用于计算任何类型的哈希码的参数化(通用)类.而不是重写Object.hashCode(),我们将使用这样的泛型类:
public final class Hash<A> {
private final F<A, Integer> hashFunction;
public Hash(final F<A, Integer> f) {
this.hashFunction = f;
}
public int hash(A a) {
return hashFunction.f(a);
}
}
Run Code Online (Sandbox Code Playgroud)
这比使用继承更灵活,因为我们可以保持使用组合和参数多态的主题,而不会锁定脆弱的层次结构.
Java的泛型并不完美.例如,您可以抽象类型,但不能抽象类型构造函数.也就是说,您可以说"对于任何类型T",但您不能说"对于任何类型T采用类型参数A".
泛型的一个巨大的胜利是它们让你避免子类化.子类化倾向于导致易于扩展的脆弱类层次结构,以及在不查看整个层次结构的情况下难以单独理解的类.
Wereas仿制药之前,你可能有类,如Widget延长了FooWidget,BarWidget和BazWidget,与仿制药可以有一个通用类Widget<A>,需要一个Foo,Bar或Baz在其构造给你Widget<Foo>,Widget<Bar>和Widget<Baz>.
我只是喜欢它们,因为它们为您提供了一种快速定义自定义类型的方法(因为我仍然使用它们).
因此,例如,不是定义由字符串和整数组成的结构,而是必须实现关于如何访问这些结构的数组等的整套对象和方法,您可以只创建一个字典
Dictionary<int, string> dictionary = new Dictionary<int, string>();
Run Code Online (Sandbox Code Playgroud)
编译器/ IDE完成了其余的繁重工作.特别是字典允许您使用第一种类型作为键(没有重复值).
泛型的最大好处是代码重用.假设您有很多业务对象,并且您将为每个实体编写非常类似的代码来执行相同的操作.(IE Linq to SQL操作).
使用泛型,您可以创建一个类,该类能够在给定基类继承的任何类型的情况下运行,或者实现给定的接口,如下所示:
public interface IEntity
{
}
public class Employee : IEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int EmployeeID { get; set; }
}
public class Company : IEntity
{
public string Name { get; set; }
public string TaxID { get; set }
}
public class DataService<ENTITY, DATACONTEXT>
where ENTITY : class, IEntity, new()
where DATACONTEXT : DataContext, new()
{
public void Create(List<ENTITY> entities)
{
using (DATACONTEXT db = new DATACONTEXT())
{
Table<ENTITY> table = db.GetTable<ENTITY>();
foreach (ENTITY entity in entities)
table.InsertOnSubmit (entity);
db.SubmitChanges();
}
}
}
public class MyTest
{
public void DoSomething()
{
var dataService = new DataService<Employee, MyDataContext>();
dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
var otherDataService = new DataService<Company, MyDataContext>();
otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });
}
}
Run Code Online (Sandbox Code Playgroud)
注意在上面的DoSomething方法中给定不同类型的相同服务的重用.真的很优雅!
在您的工作中使用泛型还有很多其他很好的理由,这是我的最爱.
键入的集合 - 即使您不想使用它们,您也可能需要从其他库中处理它们,其他来源.
类创建中的通用类型:
公共课Foo <T> {public T get()...
避免施法 - 我一直不喜欢像
new Comparator {public int compareTo(Object o){if(o instanceof classIcareAbout)...
你实际上在检查一个应该只存在的条件,因为接口用对象表示.
我对仿制药的最初反应与你的相似 - "太乱,太复杂".我的经验是,在使用它们之后你会习惯它们,没有它们的代码感觉不那么明确,而且不太舒服.除此之外,java世界的其余部分使用它们,所以你最终必须得到程序,对吧?
举个好例子.想象一下,你有一个名为Foo的课程
public class Foo
{
public string Bar() { return "Bar"; }
}
Run Code Online (Sandbox Code Playgroud)
示例1 现在您想拥有一个Foo对象的集合.您有两个选项,LIst或ArrayList,它们都以类似的方式工作.
Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();
//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,如果我尝试添加一个FireTruck类而不是Foo,ArrayList将添加它,但是Foo的通用列表将引发异常.
例二.
现在你有两个数组列表,并且你想在每个列表上调用Bar()函数.由于hte ArrayList中填充了Objects,因此必须先抛出它们才能调用bar.但由于Foo的通用列表只能包含Foos,因此可以直接调用Bar().
foreach(object o in al)
{
Foo f = (Foo)o;
f.Bar();
}
foreach(Foo f in fl)
{
f.Bar();
}
Run Code Online (Sandbox Code Playgroud)