Code Poet
Code Poet

Reputation: 11447

Java Generics: Can't get syntax right

I'm trying to write a custom UI component (on android) that will display a text edit control along with two buttons which can be clicked up or down. Up increments the number in the edit control and down decrements it.

I'd like this class to be generic on the numeric type i.e. i'd like to specialize the class for int and float types.

I've been reading this nice Java Generics Tutorial but my particular implementation doesn't seem to compile. Here's what I have got:

public class NumberPicker<T> extends LinearLayout implements OnClickListener,
        OnFocusChangeListener, OnLongClickListener {

    public interface OnChangedListener<T> {
        void onChanged(NumberPicker<T> picker, T oldVal, T newVal);
    }

    public interface Formatter<T> {
        String toString(T value);
    }

    // Error: Cannot make a static reference to the non-static type T
    public static final NumberPicker.Formatter<T> TWO_DIGIT_FORMATTER =
            new NumberPicker.Formatter<T>() {
                public String toString(T value) {
                        // Do something with T value
                }
        };
}

I'm getting the following compiler error in the declaration of the TWO_DIGIT_FORMATTER:

Cannot make a static reference to the non-static type T

Can someone please help me with the syntax. Coming from C++ I understand generic well enough and am aware of some of the differences between the two. I just need help with the syntax.

I tried changing to:

        // "Syntax error on token "int", Dimensions expected after this token"
    public static final NumberPicker.Formatter<int> TWO_DIGIT_FORMATTER =
            new NumberPicker.Formatter<int>() {
                public String toString(int value) {
                    return mFmt.toString();
                }
        };

This gives "Syntax error on token "int", Dimensions expected after this token"

Upvotes: 1

Views: 1280

Answers (4)

biziclop
biziclop

Reputation: 49814

The T parameter belongs to a NumberPicker instance, each instance can have a different value to it, so it can't be relied on in a static field of the class.

Nor can it be referenced in the public interface Formatter<T>, as interface members are by default static.

One way to work around this is:

  1. TWO_DIGIT_FORMATTER, being a constant should have the parameter bound. When you think about it, an unbound parameter doesn't really make much sense in this situation.
  2. Formatter can have its own separate parameter, checked when you pass it to a NumberPicker instance. (This is already done implicitly in OnChangedListener.)

So your code will be something like this:

public class NumberPicker<T>  {

    public interface OnChangedListener<Y> { //
        void onChanged(NumberPicker<Y> picker, Y oldVal, Y newVal);
    }

    public interface Formatter<X> {
        String toString(X value);
    }

    public void setFormatter( Formatter<T> formatter ) { ... }

    public static final NumberPicker.Formatter<? extends Number> TWO_DIGIT_FORMATTER =
        new NumberPicker.Formatter<Number>() {
            public String toString(Number value) {
                    // Do something with T value
            }
        };

}

I replaced T with X and Y in the interface definitions to avoid confusion. The parameter of the interfaces is completely separate from the parameter of NumberPicker By giving them different names this becomes readily apparent.

Upvotes: 1

alexey28
alexey28

Reputation: 5230

You cannot use generic in static method. Generic will paremetrize your instances, and static is not instance but class scope.

You can change it to (remove static):

public final NumberPicker.Formatter<T> TWO_DIGIT_FORMATTER = ...

Upvotes: 0

Marko Topolnik
Marko Topolnik

Reputation: 200306

Your field TWO_DIGIT_FORMATTER is a static field, that is, a class field, not belonging to any specific instance. On the other hand, the type parameter T is getting resolved only when you refer to a specific instance of NumberPicker where you have bound T to a concrete type. The type of T changes with different instances so naturally it has no meaning on a class field. You'll have to remove the static qualifier from TWO_DIGIT_FORMATTER and then you'll have to access it through an instance of the class.

Upvotes: 0

ColinD
ColinD

Reputation: 110104

This doesn't have to do with syntax, it has to do with the fact that your type T is associated with instances of NumberPicker, while your TWO_DIGIT_FORMATTER is static and as such not associated with any instance of NumberPicker. So there is no type T for it to refer to.

Upvotes: 2

Related Questions