简体   繁体   English

为泛型类型参数传入数组

[英]Passing in an array for a generic type parameter

public class C<T>{

    private T a;

    public C(){

    }


}

In my Java textbook, it says the type parameter T can be any reference type, including an array type.在我的 Java 教科书中,它说类型参数 T 可以是任何引用类型,包括数组类型。 I am trying to code an example to demonstrate this functionality (T being an array), but I do not know how.我正在尝试编写一个示例来演示此功能(T 是一个数组),但我不知道如何。 In my driver I do在我的司机我做

C<int[]> o = new C<int[]>();

and it compiles but I do not know how to work with the T being int[] inside the C class definition.它可以编译,但我不知道如何在 C 类定义中使用 int[] 的 T。 How would I make it, for example, create an array of 5 random integers and print them?例如,我将如何创建一个包含 5 个随机整数的数组并打印它们? Any meaningful demonstration of an array being subbed in for T will do actually.将数组替换为 T 的任何有意义的演示实际上都可以。

The way you can achieve this is by declaring field a as T[] type inside the class C : 你可以通过在类C声明字段aT[]类型来实现这一点:

class C<T>{
    private T[] a;
    public C(T[] a){
        this.a = a;
    }
    public void set(int index, T value) {
        a[index] = value;
    }
}

Because your class's a member is private , you can't actually do anything with it. 因为你的类的a成员是private ,你不能真正用它做任何事情。 Typically, you access and modify a simple class's private members with getters and setters, respectively. 通常,您分别使用getter和setter访问和修改简单类的私有成员。 Let's add a bit to your class: 让我们为你的课程添加一些内容:

public class C<T>{

    private T a;

    public C(){

    }

    public T getA() {
        return a;
    }

    public void setA(T a) {
        this.a = a;
    }
}

Now we have a generic getter/setter for our generic field. 现在我们为通用字段设置了一个通用的 getter / setter。

In your driver, you can now do this: 在您的驱动程序中,您现在可以执行此操作:

C<int[]> o = new C<int[]>(); // create our generic object
int[] myA = new int[5];      // create an array of ints
o.setA(myA);                 // set the member in our object
// do whatever you want with myA

Now if other objects, methods, or classes handle o , they can all call o.getA to handle the same array. 现在,如果其他对象,方法或类处理o ,它们都可以调用o.getA来处理相同的数组。 This is just one (contrived) example of how to work with generics. 这只是如何使用泛型的一个(人为的)示例。

Making a generic type an array type tends to be useful only for storing and retrieving them. 使泛型类型成为数组类型往往仅用于存储和检索它们。 This is because a generic type is meant to be worked with as if the type isn't known. 这是因为泛型类型的使用方式就好像类型未知。 I mean to say that if you make T an array type, like int[] , you can't work with T as if it were an array, because T could be of any type, not just an array. 我的意思是说,如果你使T成为一个数组类型,比如int[] ,你就不能像使用T一样使用T ,因为T可以是任何类型,而不仅仅是数组。


Giving you a useful example is a lot more complicated. 给你一个有用的例子要复杂得多。 First thing that comes to mind is maybe some kind of table of rows: 首先想到的可能是某种行表:

public class GenericTable<T> {
    private T[] rows;

    public GenericTable(int numRows) {
        this.rows = new T[numRows];
    }

    public T getRow(int position) { 
        return rows[position];
    }

    public setRow(T row, int position) {
        this.rows[position] = row;
    }

    public void sortRows(Comparator<T> sortFunction) {
         Arrays.sort(rows, sortFunction);
    }

    public int size() {
        return rows.length;
    }
}

Above we have a generic class which does not have a getter/setter, but allows us to set rows and sort the data as we like. 上面我们有一个没有 getter / setter的泛型类,但允许我们按照自己的意愿设置行和排序数据。

In our driver, we can make a table of int arrays. 在我们的驱动程序中,我们可以创建一个int数组表。 This is a lot like a 2D array ( int[][] ), but our GenericTable abstracts one of the dimensions which is easier to read: 这很像2D数组( int[][] ),但是我们的GenericTable抽象了一个更易于阅读的维度:

GenericTable<int[]> table = new GenericTable<>(5); // 5 rows of int[]s
for (int iRow = 0; iRow < table.size(); i++) {
    int length = /* a random int */;
    int[] myNums = new int[length];
    // insert a bunch of numbers into myNums
    table.setRow(myNums, iRow);
}

Now our table is filled, but let's say we want to sort the table based on the length of each row. 现在我们的表已经填满了,但是假设我们想根据每行的长度对表进行排序。 Thanks to our generic definition of table sorting, this is as simple as: 由于我们对表排序的通用定义,这很简单:

table.sortRows(Comparator.comparing(int[]::length));

Now we might pass this table to another class, which does something else generically: 现在我们可以将这个表传递给另一个类,它通常会执行其他操作:

myTableDisplay.writeTable(table);

And maybe myTableDisplay.writeTable looks something like this: 也许myTableDisplay.writeTable看起来像这样:

public void writeTable(GenericTable<?> table) {
    // open a Object writer resource or something
    for (int iRow = 0; iRow < table.size(); iRow++) {
        Object row = table.getRow(iRow);
        // write the row with the resource
    }
}

Now writeTable doesn't need to know or care what kind of table is being written. 现在writeTable不需要知道或关心正在编写什么类的表。 This demonstrates the (admittedly limited) usability of T being an int[] , since only our driver cares that type T is int[] in this case, but no code anywhere else does. 这证明了T是一个int[]的(通常是有限的)可用性,因为在这种情况下只有我们的驱动程序关心类型Tint[] ,但是其他地方没有代码。

You snippet is kinda describing the simplest of the containers, where there is only one element of arbitrary T . 你的代码片段有点描述了最简单的容器,其中只有一个任意T元素。 However since a is private is simply impossible for it to take any value apart from null unless you add a setter or make it non-private. 然而,因为a是私有的,除了你添加一个setter或者将它设置为非私有之外,它除了null之外几乎不可能取任何值。 So a set of close to minimal changes to make it a bit more worth it would be: 因此,一组接近最小的变化使其更有价值:

public class C<T> {

   private T a;

   public C() {};

   public T get() { return a; }

   public void set(T value) { a = value; }

}

Notice that generic containers lack the ability to instantiate a instance of T directly since their code cannot make assumptions about what T actually is. 请注意,通用容器缺乏直接实例化T实例的能力,因为它们的代码无法假设T实际上是什么。 This true for C here as it is for any other containers in the standard Java library ( java.util.* ). 对于C ,这适用于标准Java库( java.util.* )中的任何其他容器。 So any value that the container may eventually contain must be created somewhere else and provided by the invoking code. 因此,容器最终可能包含的任何值都必须在其他位置创建,并由调用代码提供。 In the example above, whatever is using the C typed instance would pass such an object using the setter: 在上面的示例中,使用C类型实例的任何内容都将使用setter传递此类对象:

class Blah {
  // ...
  void someMethod(...) {
      C<int[]> c = new C<int[]>();

      int[] fiveValues = new int[] {1, 2, 3, 4 ,5};

      c.set(FiveValues);
  } 
  // ...
}

You never will see something like new T() or new T[n] in the code of the container, it won't ever compile. 你永远不会在容器的代码中看到类似new T()new T[n]的东西,它将永远不会编译。 The only way to create such instance within an invocation of a method in C is by delegating in some kind of factory object that is passed also either once at the construction of the C instance or passed as a parameter of whatever method is responsible to the creation. C调用方法中创建此类实例的唯一方法是委托某种工厂对象,该工厂对象在构造C实例时也会传递一次,或者作为任何负责创建的方法的参数传递。

And as for other task like compose a string representation it is possible to add a routine in C that enquires for the type of the T element class at run time and come out with an appropriate way to display it. 至于编写字符串表示的其他任务,可以在C中添加一个例程,该例程在运行时查询T元素类的类型,并以适当的方式显示它。 How ever that would end up with if else if else if .... else statement that may break as the method has to support more unforeseen types. 然而, if else if else if .... else语句可能会破坏,因为该方法必须支持更多不可预见的类型。 Normally it makes more sense to delegate that task also to the invoking class that is aware of the actual type T . 通常,将该任务委托给知道实际类型T的调用类更有意义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM