Search code examples
javaandroidgenericsandroid-arrayadapter

Why getItem(position) should be casted inside a generic class (i.e. Custom ArrayAdapter<T>)?


I'm trying to understand generics in Java. I'm working in Android studio. I have a Word class and I have an ArrayList of Word objects.

ArrayList<Word> words = new ArrayList<>();

I tried to make a generic class like this

public class wordAdapter<T> extends ArrayAdapter<T> {
    public wordAdapter(Context context, ArrayList<T> object) {
        super(context, 0, object);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Get the data item for this position
        Word wordItem = getItem(position); 
        // This line gives an error because it needs to be casted to "Word"
        // Rest of the code
    }
}

Now, my question is why the compiler complaining? When I use this class I will pass an ArrayList containing Word objects. So, as far as I know the following line of code:

wordAdapter<Word> adapter = new wordAdapter<>(this, words);

Should transfer the type parameter <T> to be <Word> .
I don't get it why the compiler treats the objects obtained from getItem(position) as Object object not as Word objects. Plus I wonder since it is a generic, shouldn't the compiler ignore these things? How can it recognize what kind of objects I will deliver?


Solution

  • If defined like this

    public class MyClass<T> extends SomeClass<T>{
    
      public T someMethod(){
         // T could be any type. Could be Word, could be String, ......
         T someObject = super.getItem();
         return someObject
      }
    
    }
    

    T is an undefined type inside the class MyClass because it is defined generic. Later on any type could be passed.

    MyClass<Word> instance1 = new MyClass<Word>();
    MyClass<String> instance2 = new MyClass<String>();
    
    Word value1 = instance1.someMethod();
    String value2 = instance2.someMethod();
    

    So while operating inside MyClass you don't know what type will be defined later on.

    What you need to do is to define the type inside your class to be able to use it. So instead of passing the generic type later on from the outside, you define it already on the inside

    public class WordClass extends SomeClass<Word>{
    
      public Word someMethod(){
        // since the generic type is now defined as Word, you can reference it as such
        Word wordObject = super.getItem();
        return wordObject;
      }
    
    }
    

    Since in your case the type is unknown but you are trying to define it as Word, you need to add a so called cast. In other words telling java to look at the returned unknown type as a object of the word type. If you only use your class for Word it would work but is ugly and unnecessary. But if you would use it later for lets say Strings it would break since a String object can't be casted to a word object

    EDIT:
    I just read in one of your comments that you would like to create a generic class for all kind of datatypes which can use the type-specific functions.

    Well technically this is possible by doing type-checks and then cast them. But this would be very very ugly.

    public class UglyAdapter<T> extends ArrayAdapter<T>{
    
      public void doTypeSpecificStuff(T obj){
        Class<T> objectClass = obj.getClass();
        if(objectClass.equals(Integer.class)){
          Integer intObj = (Integer) obj;
          // here you can call Integer-specific methods
        }else if(objectClass.equals(String.class)){
          String strObj = (String) obj;
          // here you can call String-specific methods
        }
        else if(objectClass.equals(Word.class)){
          Word wordObj = (Word) obj;
          // here you can call Word-specific methods
        }
        else{
          // object is unsupported type. Throw exception or do whatever
        }
      }
    
    }
    

    However the usual way to go about this kind of stuff is to create one adapter for each type you need to support. Doing it this way is very ugly and shouldn't be done at all.