it-swarm.cn

对于仿制药有什么好处,为什么要使用它们呢?

我以为我会把这个垒球提供给任何想要从公园里击出的人。什么是泛型,泛型的优点是什么,为什么,在哪里,我应该如何使用它们?请保持相当基本。谢谢。

79
MrBoJangles
  • 允许您编写类型安全的代码/使用库方法,即List <string>保证是字符串列表。
  • 由于使用了泛型,编译器可以对代码执行编译时检查以确保类型安全,即您是否尝试将int放入字符串列表中?使用ArrayList会导致运行时错误不太透明。
  • 比使用对象更快,因为它避免装箱/拆箱(其中.net必须将 值类型转换为引用类型,反之亦然 )或从对象转换为所需的引用类型。
  • 允许您编写适用于具有相同基础行为的许多类型的代码,即Dictionary <string,int>使用与Dictionary <DateTime,double>相同的底层代码;使用泛型,框架团队只需要编写一段代码来实现两种结果,并具有上述优点。
120
ljs

我真的不想重复自己。我讨厌经常输入相同的东西。我不喜欢多次重复处理,略有不同。

而不是创建:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

我可以构建一个可重用的类...(在你不想因为某些原因使用原​​始集合的情况下)

class MyList<T> {
   T get(int index) { ... }
}

我现在效率提高了3倍,我只需保留一份。为什么你不想保持更少的代码?

对于非集合类也是如此,例如Callable<T>Reference<T>必须与其他类交互。你真的想扩展Callable<T>Future<T>以及其他所有相关的类来创建类型安全的版本吗?

我不。

45
James Schek

如果你在1.5版本发布之前搜索Java bug数据库,你会发现NullPointerException的错误比ClassCastException多7倍。所以它似乎不是一个很好的功能,找到错误,或至少在一些烟雾测试后仍然存在的错误。

对我来说,泛型的巨大优势在于它们记录 代码 重要类型信息。如果我不想在代码中记录该类型信息,那么我将使用动态类型语言,或者至少使用具有更隐式类型推断的语言。

保持对象的集合本身并不是一种糟糕的风格(但是通常的风格是有效地忽略封装)。这取决于你在做什么。将集合传递给“算法”稍微容易用泛型检查(在编译时或编译时)。

15
Tom Hawtin - tackline

Java中的泛型促进 参数多态 。通过类型参数,您可以将参数传递给类型。正如像String foo(String s)这样的方法模拟某些行为,不仅仅是针对特定字符串,而是针对任何字符串s,因此像List<T>这样的类型会模拟某些行为,不仅针对特定类型,而且 对于任何类型 List<T>表示 对于任何类型T,有一种List,其元素是Ts 。所以List实际上是一个 类型的构造函数 。它将一个类型作为参数,并构造另一个类型作为结果。

以下是我每天使用的泛型类型的几个示例。首先,一个非常有用的通用接口:

public interface F<A, B> {
  public B f(A a);
}

这个接口说 对于某些两种类型,AB,有一个函数(称为f),它接受一个A并返回一个B实现此接口时,AB可以是您想要的任何类型,只要您提供一个函数f,它接受前者并返回后者。这是界面的示例实现:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

在泛型之前,多态性是通过 subclassing 使用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);
  }
}

这比使用继承更灵活,因为我们可以保持使用组合和参数多态的主题,而不会锁定脆弱的层次结构。

Java的泛型并不完美。例如,您可以抽象类型,但不能抽象类型构造函数。也就是说,您可以说“对于任何类型T”,但您不能说“对于任何类型T采用类型参数A”。

我在这里写了一篇关于Java泛型的这些限制的文章。

泛型的一个巨大的胜利是它们让你避免子类化。子类化倾向于导致易于扩展的脆弱类层次结构,以及在不查看整个层次结构的情况下难以单独理解的类。

在泛型之前,你可能有像WidgetFooWidgetBarWidget扩展的类BazWidget这样的类,使用泛型你可以有一个泛型类Widget<A>,它在构造函数中带有FooBarBaz,为你提供Widget<Foo>Widget<Bar>Widget<Baz>

10
Apocalisp

泛型可以避免拳击和拆箱的性能损失。基本上,请查看ArrayList与List <T>。两者都做同样的核心事情,但List <T>会快得多,因为你不必对象/来自对象。

8
Darren Kopp
  • 键入的集合 - 即使您不想使用它们,您也可能需要从其他库中处理它们,其他来源。

  • 类创建中的通用类型:

    公共课Foo <T> {public T get()...

  • 避免施法 - 我一直不喜欢像

    new Comparator {public int compareTo(Object o){if(o instanceof classIcareAbout)...

你实际上在检查一个应该只存在的条件,因为接口用对象表示。

我对仿制药的最初反应与你的相似 - “太乱,太复杂”。我的经验是,在使用它们之后你会习惯它们,没有它们的代码感觉不那么明确,而且不太舒服。除此之外,Java世界的其余部分使用它们,所以你最终必须得到程序,对吧?

5
Steve B.

举个好例子。想象一下,你有一个名为Foo的课程

public class Foo
{
   public string Bar() { return "Bar"; }
}

示例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());

在上面的代码中,如果我尝试添加一个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();
}
5
Peter Lange

我只是喜欢它们,因为它们为您提供了一种快速定义自定义类型的方法(因为我仍然使用它们)。

因此,例如,不是定义由字符串和整数组成的结构,而是必须实现关于如何访问这些结构的数组等的整套对象和方法,您可以只创建一个字典

Dictionary<int, string> dictionary = new Dictionary<int, string>();

编译器/ IDE完成了其余的繁重工作。特别是字典允许您使用第一种类型作为键(没有重复值)。

5
Tom Kidd

泛型的最大好处是代码重用。假设您有很多业务对象,并且您将为每个实体编写非常类似的代码来执行相同的操作。 (I.E Linq对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" });

    }
}

注意在上面的DoSomething方法中给定不同类型的相同服务的重用。真的很优雅!

在您的工作中使用泛型还有很多其他很好的理由,这是我最喜欢的。

5
Dean Poulin

你有没有写过一个方法(或类),其中方法/类的关键概念没有紧密绑定到参数/实例变量的特定数据类型(想想链表,最大/最小函数,二进制搜索等)。

你有没有希望你可以重复使用algorthm /代码而不需要使用cut-n-paste重用或者破坏强类型(例如我想要一个List的字符串,而不是List的东西我 希望 是字符串!)?

这就是为什么你应该 想要 使用泛型(或更好的东西)。

4
Bert F

不要忘记,泛型不仅仅被类使用,它们也可以被方法使用。例如,请使用以下代码段:

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

它很简单,但可以非常优雅地使用。好的是该方法返回它给出的任何内容。当您处理需要重新抛回调用者的异常时,这会有所帮助:

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

关键是你不会通过传递方法丢失类型。您可以抛出正确类型的异常,而不仅仅是Throwable,这可以是没有泛型的所有操作。

这只是一个用于泛型方法的简单示例。使用泛型方法可以做很多其他巧妙的事情。在我看来,最酷的是用泛型来推断类型。以下示例(摘自Josh Bloch的Effective Java第2版):

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

这并没有做太多,但是当泛型类型很长(或嵌套;即Map<String, List<String>>)时,它确实减少了一些混乱。

3
jigawot

从Sun Java文档中,回应“我为什么要使用泛型?”:

“泛型提供了一种方法,您可以将集合的类型传递给编译器,以便可以检查它。一旦编译器知道集合的元素类型,编译器就可以检查您是否已经一致地使用了集合并且可以插入从集合中取出的值的正确转换...使用泛型的代码更清晰,更安全.... 编译器可以在编译时验证在运行时不违反类型约束 [强调我的因为程序在没有警告的情况下编译,我们可以肯定地说它不会在运行时抛出ClassCastException。使用泛型的净效果,特别是在大型程序中, 提高了可读性和稳健性 。[强调我的]”

2
Demi

无论如何jvm强制转换......它隐式地创建了将泛型类型视为“对象”的代码,并为所需的实例化创建了强制转换。 Java泛型只是语法糖。

2
Anonymous Coward

正如Mitchel指出的那样,主要优势是强类型,无需定义多个类。

这样你就可以做到这样的事情:

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

如果没有泛型,则必须将blah [0]转换为正确的类型才能访问其函数。

2
Kevin Pang

我知道这是一个C#问题,但是 泛型 也用于其他语言,它们的使用/目标非常相似。

Java集合使用 泛型 自Java 1.5以来。因此,使用它们的好地方是在创建自己的类似集合的对象时。

我几乎到处看到的一个例子是Pair类,它包含两个对象,但需要以通用方式处理这些对象。

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

无论何时使用此Pair类,您都可以指定希望它处理哪种对象,并且任何类型转换问题都将在编译时显示,而不是运行时。

泛型也可以使用关键字“super”和“extends”定义其边界。例如,如果要处理泛型类型但是要确保它扩展了一个名为Foo的类(具有setTitle方法):

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

虽然它本身并不是很有趣,但知道每当你处理FooManager时,你知道它将处理MyClass类型,并且MyClass扩展了Foo,这是很有用的。

2
etchasketch

我在使用SpringORM和Hibernate实现的GenericDao中使用它们,如下所示

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

通过使用泛型,我对这个DAO的实现迫使开发人员只通过子类化GenericDao来传递它们所设计的实体。

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

我的小框架更健壮(包括过滤,延迟加载,搜索等)。我刚刚在这里简化了一个例子

我,就像史蒂夫和你一样,在开头说 “太麻烦和复杂” 但现在我看到了它的优点

1
victor hugo

泛型允许您创建强类型的对象,但您不必定义特定类型。我认为最有用的例子是List和类似的类。

使用通用列表,您可以拥有任何您想要的列表列表,您可以始终引用强类型,您不必转换或使用数组或标准列表。

1
Mitchel Sellers

泛型允许您对应该能够容纳任何对象的对象和数据结构使用强类型。当从通用结构(装箱/拆箱)中检索对象时,它还消除了繁琐且昂贵的类型转换。

使用两者的一个示例是链表。如果链表类只能使用对象Foo,它会有什么用?要实现可以处理任何类型对象的链接列表,如果希望列表仅包含一种类型的对象,则链接列表和假设节点内部类中的节点必须是通用的。

1
Steve Landey

已经提到了诸如“类型安全”和“不投射”等明显的好处,所以也许我可以谈谈其他一些“好处”,我希望它有所帮助。

首先,泛型是一个独立于语言的概念,而IMO,如果你同时考虑常规(运行时)多态,它可能会更有意义。

例如,我们从面向对象设计中了解到的多态性具有运行时概念,其中调用程序对象在运行时随着程序执行而计算出来,并且根据运行时类型相应地调用相关方法。在泛型中,这个想法有点相似,但一切都在编译时发生。这意味着什么以及如何使用它?

(让我们坚持使用泛型方法来保持它的紧凑)这意味着你仍然可以在不同的类上使用相同的方法(就像你之前在多态类中所做的那样),但这次它们是由编译器自动生成的,取决于类型集在编译时。您可以根据编译时提供的类型对方法进行参数化。因此,不是从头开始编写方法 对于每一种类型 就像在运行时多态(方法重写)中那样,您可以让编译器在编译期间完成工作。这有一个明显的优势,因为您不需要推断系统中可能使用的所有可能类型,这使得它在没有代码更改的情况下可扩展性更高。

类的工作方式几乎相同。您可以参数化类型,并且编译器会生成代码。

一旦你理解了“编译时间”,就可以使用“有界”类型,并通过类/方法限制可以作为参数化类型传递的内容。因此,您可以控制要传递的内容,这是一个强大的功能,尤其是您的框架正被其他人使用。

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

现在没有人可以设置除MyObject之外的其他东西了。

此外,您可以对方法参数“强制”类型约束,这意味着您可以确保两个方法参数都依赖于相同的类型。

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

希望所有这一切都有道理。

1
stdout

使用Generics(特别是使用Collections/Lists)的另一个好处是可以获得编译时间类型检查。当使用通用列表而不是对象列表时,这非常有用。

1
Chris Pietschmann

如果您的集合包含值类型,则在插入集合时不需要对对象进行装箱/取消装箱,这样您的性能就会大幅提升。像resharper这样的酷附加组件可以为你生成更多代码,比如foreach循环。

1
gt124

最主要的原因是他们提供 类型安全

List<Customer> custCollection = new List<Customer>;

相反,

object[] custCollection = new object[] { cust1, cust2 };

举个简单的例子。

1
Vin

总之,泛型允许您更准确地指定您打算做什么(更强的打字)。

这有几个好处:

  • 因为编译器更了解你想要做什么,它允许你省略很多类型转换,因为它已经知道类型将是兼容的。

  • 这也可以获得有关程序正确性的早期反馈。之前在运行时失败的事情(例如因为对象无法以所需类型进行转换)现在在编译时失败,您可以在测试部门提交密码错误报告之前修复错误。

  • 编译器可以进行更多优化,例如避免装箱等。

1
Thomas Danecker

添加/扩展的几件事(从.NET的角度来讲):

通用类型允许您创建基于角色的类和接口。这已经用更基本的术语说过了,但是我发现你开始使用以类型无关的方式实现的类来设计你的代码 - 这导致了高度可重用的代码。

关于方法的通用论证可以做同样的事情,但它们也有助于将“告诉不要问”原则应用于投射,即“给我我想要的东西,如果你不能,你告诉我为什么”。

1
SpongeJim

使用泛型收集只是简单而干净。即使你在其他任何地方都能找到它,收藏品的收益对我来说也是一种胜利。

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

vS

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

要么

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

只有这一点值得泛型的边际“成本”,你不必是一个通用的大师来使用它并获得价值。

0
Will Hartung

泛型还使您能够创建更多可重用的对象/方法,同时仍提供特定类型的支持。在某些情况下,您还可以获得很多性能。我不知道Java Generics的完整规范,但在.NET中,我可以在Type参数上指定约束,如实现接口,构造函数和派生。

0
Eric Schneider

我曾经谈过这个话题。您可以在 http://www.adventuresinsoftware.com/generics/ 找到我的幻灯片,代码和录音。

0
Michael L Perry