Reputation: 1608
In Java, suppose I have a method that takes in two lists of integers, and returns an arrayList of integers from the first list, based upon the second list.
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;
}
Now suppose I did something silly like change the insertion to
result.add(b.get(i));
It'd be nice if I could do something like
public ArrayList<AInteger> func(ArrayList<AInteger> A, ArrayList<BInteger> B)
to ensure that regardless of sleep deprivation, the compiler would prevent me from making this type of mistake. Now we could try
class AInteger extends Integer{}; class BInteger extends BInteger{};
and call the method with
func((ArrayList<AInteger>) A, (ArrayList<BInteger>) B);
Unfortunately, downcasting will raise a runtime exception. So, this solution would require me (I think) to rebuild A and B into new Lists with the appropriate datatype, which is expensive. Aside from being more careful, and using unit tests, is there anyway to get the compiler to distinguish between the two lists?
Do other languages such as Scala, or Haskell, provide something that allows me to recast safely without copying?
Upvotes: 0
Views: 91
Reputation: 1608
By using generics, this can be solved by modifying the function declaration to be
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;
}
This ensures that result.add(B.get(i))
raises a type error as desired.
Unfortunately... new IntegerA(int) is not allowed, which prevents new objects from being created. So resulting exported list can only contain elements from the original list (unless downcasting is performed, which nullifies any type guarantees).
Upvotes: 0
Reputation: 425238
You would be better to make the following changes:
static
, which you can do because your method doesn't require access any instance fields - it's just "code"List
, not the concrete type ArrayList
, because someone may want to pass in a LinkedList
for example - your code applies to all list types, not just ArrayList
sLike this:
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;
}
This code would allow your alternative code result.add(b.get(i))
to work.
BTW, Integer
is a final
class; you can't extend it, so you can't do this: class AInteger extends Integer
Upvotes: 0
Reputation: 17622
While I don't think you can do what you want in Java, you can refactor your method a little to avoid the problem:
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);
Upvotes: 0
Reputation: 4918
Actually C++ has a feature called private inheritance. It works with classes such that all functionality of the parent is inherited, but users can't treat it as the common parent. Unfortunately this does not work with primitives.
Java does not support a feature like this. Most languages do not support this kind of "semantic differentiation" in a straightforward way. Remember, the code generated for both classes would be equal.
Upvotes: 1
Reputation: 2725
Your question is not easy, try something like:
Receive two lists with different object types (the list items type) but "equivalent" for you and convertible in Java and the returned list contains the type you want to get items from, avoiding getting from the other list.
For example, use public ArrayList<Integer> func(ArrayList<Integer> A, ArrayList<Number> B)
this prevents you from coping items from B to the returning list, as you need to perform a downcasting explicitly. You can still evaluate B items as numbers by calling Number.intValue()
if you want ints and/or B items are really ints. the problem is the invocation if B is really defined as ArrayList<Integer>
as you need to perform an explicit cast with generics that raises a warning that can avoid putting @SupressWarning
in the method definition.
Another question is Type Erasure that prevents at runtime generic type checking as generic types are "erased" when compiling in Java. C# in .Net do not have type erasure and guarantees at runtime the types of the collections (for example).
Upvotes: 1