为通用类型参数传递数组

时间:2019-04-08 14:09:14

标签: java generics

public class C<T>{

    private T a;

    public C(){

    }


}

在我的Java教科书中,类型参数T可以是任何引用类型,包括数组类型。我正在尝试编写一个示例来演示此功能(T是数组),但我不知道如何做。在我的驱动程序中,我

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

它可以编译,但是我不知道如何在C类定义中将T作为int []使用。例如,我将如何创建一个由5个随机整数组成的数组并将其打印出来?实际上,对将数组嵌入T的任何有意义的演示都可以。

3 个答案:

答案 0 :(得分:3)

您可以通过在类a中将字段T[]声明为C类型来实现此目的:

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

答案 1 :(得分:-1)

由于您班级的a成员是private,因此您实际上无法执行任何操作。通常,您分别使用getter和setter访问和修改简单类的私有成员。让我们为您的课程添加一些内容:

public class C<T>{

    private T a;

    public C(){

    }

    public T getA() {
        return a;
    }

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

现在我们为 generic 字段提供了一个 generic getter / setter。

在驱动程序中,您现在可以执行以下操作:

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

现在,如果其他对象,方法或类处理o,则它们都可以调用o.getA来处理同一数组。这只是如何使用泛型的一个(人为)示例。

使泛型类型成为数组类型往往仅对存储和检索它们有用。这是因为要使用通用类型,就好像未知类型一样。我的意思是说,如果将T设置为数组类型,例如int[],则无法像处理T一样使用数组,因为T可能是任何类型,而不仅仅是数组。


给您一个有用的示例要复杂得多。首先想到的可能是某种类型的行表:

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;
    }
}

上面我们有一个通用类,它没有 具有getter / setter,但允许我们根据需要设置行和对数据进行排序。

在驱动程序中,我们可以创建一个int数组表。这很像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);
}

现在我们的表已填充,但假设我们要根据每行的长度对表进行排序。感谢我们对表排序的一般定义,这很简单:

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

现在,我们可以将此表传递给另一个类,该类通常执行其他操作:

myTableDisplay.writeTable(table);

也许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
    }
}

现在writeTable不需要知道或不在乎正在编写哪种表。这表明Tint[]的(公认的有限)可用性,因为在这种情况下,只有我们的驱动程序关心T类型为int[],但是其他任何地方都没有代码

答案 2 :(得分:-1)

您的代码段有点描述了最简单的容器,其中只有任意T元素。但是,由于a是私有的,因此除非您添加了setter或使其成为非私有的,否则它不可能获取除null之外的任何值。因此,进行一组接近最小的更改以使其更加值得:

public class C<T> {

   private T a;

   public C() {};

   public T get() { return a; }

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

}

请注意,通用容器缺乏直接实例化T实例的能力,因为它们的代码无法假设T的实际含义。这里的C和标准Java库(java.util.*)中的任何其他容器都是如此。因此,容器最终可能包含的任何值都必须在其他地方创建并由调用代码提供。在上面的示例中,无论使用C类型实例,都将使用setter传递此类对象:

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

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

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

您永远不会在容器的代码中看到像new T()new T[n]这样的东西,它永远不会被编译。在C中的方法调用中创建此类实例的唯一方法是委派某种工厂对象,该工厂对象在C实例构造时也传递一次或作为参数传递负责创建的任何方法。

对于其他任务,例如编写字符串表示形式,可以在C中添加一个例程,该例程在运行时查询T元素类的类型,并提供一种合适的方式来显示它。以if else if else if .... else语句结束的结果可能会中断,因为该方法必须支持更多无法预料的类型。通常,将任务也委派给知道实际类型T的调用类更有意义。