泛型并调用属于该类型的方法

时间:2013-01-13 05:58:29

标签: java generics

我一直在努力创作一个代表一副牌的课程。但是,我想以某种方式创建它,它可以是任何类型的卡片,它不需要知道它能够存储它们的哪种类型,一次洗牌并将它们画成一张它们是uno卡片,常规扑克牌或交易卡。为此,我一直在尝试一些我听说但没有使用过的东西 - 泛型。

但是,我没有运气试图让它发挥作用。在绘制卡片时,它不会实例化,填充卡片或返回正确的类型。我试过混合和匹配,我只是无法让它工作。

错误的旧代码被截断以节省空间,请查看之前的编辑内容。简介:我使用Cardable而不是T,并且缺乏一般表达泛型。

那么这将如何运作,我对泛型完全陌生。我一直在四处寻找,我一​​直听到类型擦除,并且类文字应该是一个参数和yadda yadda ......但那么ArrayList是如何做到的?为什么你只需键入ArrayList<String>()就可以正常工作而不需要像ArrayList<String>(String.GetClass())那样荒谬的事情?

感谢您的时间。

编辑:Cardable是一个类别,任何可以放入牌组的牌都会延伸。

Edit2:Perception的建议因此修复了我的代码,但我不知道如何调用填充甲板。现在我接受一个数组,但是把它放在内部就好了,我不完全确定我掌握了整个 factory 方法。

public class Deck<T extends Cardable>
{
    private ArrayList<T> cardsInDeck;

    public Deck()
    {
        cardsInDeck = new ArrayList<T>();
    }

    public void populate( T[] newCards )
    {
        cardsInDeck.clear();
        for( T card : newCards )
        {
            cardsInDeck.add( card );
        }
        shuffle();
    }

    public T drawCard()
    {
        T card = null;
        try
        {
            card = cardsInDeck.get( 0 );
        }
        catch( IndexOutOfBoundsException e )
        {
            System.out.println( "Ran out of Cards" );
            e.printStackTrace();
        }
        cardsInDeck.remove( 0 );
        return card;
    }

    public void shuffle()
    {
        ArrayList<T> newDeck = new ArrayList<T>();
        Random rand = new Random();
        while( !cardsInDeck.isEmpty() )
        {
            int index = rand.nextInt( cardsInDeck.size() );
            newDeck.add( cardsInDeck.get( index ) );
            cardsInDeck.remove( index );
        }
        cardsInDeck = newDeck;
    }

    public int getSize()
    {
        return cardsInDeck.size();
    }
}

4 个答案:

答案 0 :(得分:2)

问题是:ArrayList<E>的实现不依赖于实际类型E.这就是为什么你不需要在构造函数中传递类型(正如你所说,new ArrayList<String>(String.class))。

如果你编写一个泛型类,由于某种原因,必须确切地知道泛型类型在运行时代表什么,那么你需要在构造函数中传递类型,因为,如你所说,类型擦除不允许你从T获得课程。您需要new MyClassThatNeedToKnowItsActualTypeParameter<String>(String.class)

例如,假设一个类访问数据库并检索给定类的实例。类的实例存储在以类命名的表中。例如:

class MyRepository<T> {
  T load(int id) { ... }
}

方法load需要准确知道T在运行时是什么,因为它需要能够构造一个查询,该查询将使用T代表的实际类的名称。但是,在Java中,您无法从T获取此信息,因为T将因类型擦除而消失。此外,load方法需要一种方法来创建正确类型的实例并将数据从数据库写入其中。要创建类的实例,您可以使用反射,例如执行clazz.newInstance()。在这里,你需要确切地知道你正在处理什么类。你最终会得到这样的东西:

class MyRepository<T> 
  private final Class<T> clazz;
  MyRepository(Class<T> clazz) { this.clazz = clazz; }
  T load(int id) {
    final String tableName = clazz.getSimpleName() + "Table";
    /* connect, retrieve data, disconnect */
    final T t = clazz.newInstance(); // must be inside try/catch
    /* fill instance t with data from database somehow (using reflection probably, which, again, needs to know what clazz is */
    return t;
  }
}

...
final MyRepository<User> userRepository = new MyRepository<User>(User.class);
final User user = userRepository.load(123);
...

答案 1 :(得分:1)

你可以使用泛型,但是套牌将无法填充自己 - 它不知道要创建什么类的对象!根据您的描述,您想要的基本上是ArrayList<T>,其中T可以是任何内容;它甚至不必是卡片。但是,你需要的是一种填充甲板的方法。为此,可以使用工厂对象:

public interface CardGenerator<T> {
    T[] generateAllCards();
}

public class Deck<T> {
    private ArrayList<T> cardsInDeck;

    public Deck(CardGenerator<T> generator) {
        cardsInDeck = new ArrayList<T>();
        cardsInDeck.addAll(generator.generateAllCards());
    }
    . . .
}

如果您愿意,可以将T的类型限制为Cardable,但就Deck的逻辑而言(就您所描述的而言),这不是没必要。

答案 2 :(得分:0)

查看代码我不认为你真的需要一个泛型类,因为你很难使用T. 如果您只是将填充方法更改为类似的工作,那么您的工作应该完成:

public void populate(Cardable t)
    {
        cardsInDeck.clear();
        Cardable[] cardsMade;

        cardsMade = t.makeAllCards();

        for( Cardable card : cardsMade )
        {
            cardsInDeck.add( card );
        }


        shuffle();
    }

答案 3 :(得分:0)

不要使用泛型。

如果你想处理不同的类型卡,你只会使用遗传学,例如

  • 52 card deck
  • 塔罗牌
  • 63卡5手500甲板

此问题不适合您的情况。