3

I have a class PictureArrayAdapter that extends ArrayAdapter<Pair<String, ImageInitialiser>> and has the following constructor:

public PictureArrayAdaptor(Context context, Pair<String, ImageInitialiser>[] values)

We have explicitly declared that when this constructor is called, the programmer has to pass a Pair<String, ImageInitialiser>[], otherwise a type error may occur. Now, producing such an object without a warning is rather difficult:

@SuppressWarnings("unchecked")
Pair<String, ImageInitialiser> tableData[] = new Pair[1];
tableData[0]=new Pair<String, ImageInitialiser>("A", new ResourceImageInitialiser(R.drawable.sample1));

One possibility would be to use a list instead. However, for the sake of consistency, I'd like to keep all the constructors exactly the same as the base class. Is there a nicer way of calling this constructor? I really don't think that this is how it is supposed to be called.

3
  • Is using public PictureArrayAdaptor(Context context, Pair<String, ImageInitialiser>... values) an option? Commented Aug 15, 2012 at 1:38
  • @dasblinkenlight: Thanks. That's an interesting idea, but doesn't work if I want to keep the constructors the same as the base class Commented Aug 15, 2012 at 1:40
  • I am pretty certain that passing an array in place of a vararg list: the ... is "syntactic sugar" on top of a simple array. Commented Aug 15, 2012 at 1:43

3 Answers 3

4

Another approach is to create a class that extends Pair, for example

class NamedImage extends Pair<String, ImageInitialiser> { ... }
class PictureArrayAdapter extends ArrayAdapter<NamedImage> { ... }

NamedImage is now a reifiable type so you can use arrays like NamedImage[] with impunity. In addition, it gets rid of unchecked exceptions and simplifies all your declarations so that they no longer have nested type parameters.

Sign up to request clarification or add additional context in comments.

7 Comments

Any chance you can get this into the language, a la Haskell's newtype? :)
I don't know, since I don't know Haskell. NamedImage is a real type, but I suppose there's some boilerplate (e.g., constructors) that's necessary to make it work right. Probably the most likely evolution of Java in this area is reified generics. There are a bunch of ideas about this but no concrete plans.
newtype in Haskell defines a new type whose only constuctor is an instance of the old type (translating to Java-speak), and with only a getter. That means that the type only exists at compile-time; at runtime, it's just the underlying type. Probably wouldn't work in Java, though, because of the complications with reflection.
Thanks for the info. Hm, sounds like more erasure. :-) I think we want to move in the direction of less erasure. If you're really interested in this, though, you might try to talk it up on one of the OpenJDK mailing lists.
I hadn't quite thought of it like that, but I suppose it is. Anyway, my original comment was almost entirely joking. I don't love the pattern of manually reifying types, but I understand why it's needed. I would vote for reified generics before an erasure-based newtype... but I know they're the cold fusion of Java. ;-)
|
2

Arrays and generics are like oil and water. It's impossible to guarantee type safety with an array of a generic type, hence the warnings. The same goes for varargs (...) since that involves implicit array creation. See the generics FAQ for a detailed explanation on why.

I would highly recommend making the parameter a List instead of an array. If the super constructor must take an array, you can call toArray on the List. Collections in general are much more versatile than arrays, and some people believe arrays shouldn't be directly used anymore unless absolutely necessary.

Comments

0

Ok, I've thought about it for a while and I realised that the way I am doing this, is precisely the way to do it. Lists are definitely much nicer to use when it comes to generic objects, but I am trying to match an interface for consistency.

The generics tutorial (page 15), explains that the reason we can't create arrays of generic objects is that this would allow a runtime type error without a warning.

List<?>[] lsa = new List<?>[10]; // ok, array of unbounded wildcard type
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // correct
String s = (String) lsa[1].get(0); // run time error, but cast is explicit

By using @SuppressWarnings("unchecked"), we force anyone touching this code to have to think about type safety instead of relying on the compiler. This isn't a big deal, as in languages like Python, there is no compile-time type safety at all. The @SuppressWarnings makes it clear to the programmer that they have to manage it themselves, but even if they miss this, it can still be caught at run time.

Now, let's consider the constructor. I think this would be a good place for the compiler to give a warning, because there isn't that much difference between declaring a generic array variable and a generic array argument. Here, we don't have the @SuppressWarnings, but I still think that the type itself gives the programmer enough of a warning. Besides, it is better to risk someone changing this class introducing a type error, than to make it just take a generic array and risk any caller making a type error.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.