区分等效数据类型

时间:2011-09-10 19:55:47

标签: java types

在Java中,假设我有一个方法,它接收两个整数列表,并根据第二个列表从第一个列表返回一个整数的arrayList。

public ArrayList<Integer> func(ArrayList<Integer> A, ArrayList<Integer> B){
   ArrayList result = new ArrayList();

   //A and B have the same length
   for(int index = 0; index < A.length();index++){

      //Decide if a gets added to result based on the corresponding B
      if(decisionFunction(B.get(i)){
          result.add(a.get(i));
      }
   }

 return result;
}

现在假设我做了一些愚蠢的事情,比如将插入更改为

        result.add(b.get(i));

如果我可以做类似

的事情,那就太好了
public ArrayList<AInteger> func(ArrayList<AInteger> A, ArrayList<BInteger> B)

确保无论睡眠不足,编译器都会阻止我犯这种错误。现在我们可以试试

class AInteger extends Integer{}; class BInteger extends BInteger{};

并使用

调用方法
func((ArrayList<AInteger>) A, (ArrayList<BInteger>) B);

不幸的是,向下转换会引发运行时异常。因此,这个解决方案需要我(我认为)将A和B重建为具有适当数据类型的新列表,这很昂贵。除了更加小心,并使用单元测试之外,还有什么方法可以让编译器区分这两个列表?

Scala或Haskell等其他语言是否提供了允许我安全重铸而无需复制的东西?

5 个答案:

答案 0 :(得分:1)

你的问题并不容易,请尝试以下方法:

接收两个具有不同对象类型的列表(列表项类型),但是对于您来说是“等效”并且可以在Java中转换,并且返回的列表包含您要从中获取项目的类型,从而避免从其他列表中获取。

例如,使用public ArrayList<Integer> func(ArrayList<Integer> A, ArrayList<Number> B)这可以防止您将项目从B复制到返回列表,因为您需要明确执行向下转换。如果您想要整数和/或B项真的是整数,您仍然可以通过调用Number.intValue()来评估B项目作为数字。如果B实际定义为ArrayList<Integer>,则问题是调用,因为您需要使用泛型执行显式强制转换,从而引发警告,避免将@SupressWarning放入方法定义中。

另一个问题是Type Erasure在运行时阻止泛型类型检查,因为在Java中编译时,泛型类型被“擦除”。 .Net中的C#没有类型擦除,并且在运行时保证集合的类型(例如)。

答案 1 :(得分:1)

实际上C ++有一个称为私有继承的功能。它适用于类,以便继承父级的所有功能,但用户不能将其视为公共父级。不幸的是,这不适用于原语。

Java不支持这样的功能。大多数语言都不直接支持这种“语义区分”。请记住,为两个类生成的代码都是相同的。

答案 2 :(得分:0)

虽然我认为你不能在Java中做你想做的事,但你可以稍微重构一下你的方法来避免这个问题:

void func(ArrayList<Integer> B /* input only */ , 
          ArrayList<Integer> A /* input-output */) {
     // remove some elements from A as necessary
}

...

ArrayList<Integer> result = new ArrayList<Integer>();
result.addAll(A);
func(B, result);

答案 3 :(得分:0)

您最好进行以下更改:

  • 键入返回类型,以便安全地向其添加整数
  • 制作方法static,您可以这样做,因为您的方法不需要访问任何实例字段 - 它只是“代码”
  • 使用 abstract 类型声明您的参数和返回类型 - 即List,而不是具体类型ArrayList,因为有人可能想要传递LinkedList例如 - 您的代码适用于所有列表类型,而不仅仅是ArrayList s
  • 使用小写字母命名变量 - 它遵循通常的样式,以便其他人可以更舒适地阅读您的代码

像这样:

public static List<Integer> func(List<Integer> a, List<Integer> b) {
    List<Integer> result = new ArrayList<Integer>();

    for (int index = 0; index < a.length(); index++) {
       if (decisionFunction(b.get(i)) {
           result.add(a.get(i));
       }
    }

    return result;
}

此代码允许您使用替代代码result.add(b.get(i))

BTW,Integerfinal类;你不能扩展它,所以你不能这样做:class AInteger extends Integer

答案 4 :(得分:0)

通过使用泛型,可以通过将函数声明修改为

来解决
public <IntegerA extends Integer, IntegerB extends Integer>
   List<IntegerA> func(List<IntegerA> A, List<IntegerB> B){
       ArrayList result = new ArrayList();

       //A and B have the same length
       for(int index = 0; index < A.length();index++){

           //Decide if a gets added to result based on the corresponding B
           if(decisionFunction(B.get(i)){
               result.add(A.get(i));
           }
       }

       return result;
}

这可确保result.add(B.get(i))根据需要引发类型错误。

不幸的是......不允许使用新的IntegerA(int),这会阻止创建新对象。因此,导出的导出列表只能包含原始列表中的元素(除非执行向下转换,这会使任何类型保证无效)。