0

I have a Adapter in my Android app and I want to make it Generic.

Basiclly the Adapter looks like that:

public class myAdapter extends FragmentStatePagerAdapter {

DataTypaA myFragment;
DataTypeB data;
DataTypeC items;

public myAdapter(FragmentManager fm, DataTypaA fragment) {
        data = new SparseArray<DataTypeB>();
        myFragment = fragment;
        items = myFragment.getData();
    }

    public DataTypeB getItem(int position) {
        return data.get(position);
    }

    @Override
    public int getCount() {
        return items.getList().size();
    }


public void setData () {

items = myFragment.getItems ()  //getItems return DataTypeC
data.setTheData (items) 
}
}

I changed it to generic

public class myAdapter <A,B,C> extends FragmentStatePagerAdapter {

A myFragment;
B data;
C items;

public myAdapter(FragmentManager fm, A fragment) {
        data = new SparseArray<B>();
        myFragment = fragment;
        items =  (C) myFragment.getData();
    }

    public B getItem(int position) {
        return data.get(position);
    }

    @Override
    public int getCount() {
        return items.getList().size();
    }


public void setData () {

items = myFragment.getItems ()  //getItems return DataTypeC
data.setTheData (items) 
}
}

but I'm getting different errors, when methods should get DataTypeC parameter and I passing it parameter type C ( data.setTheData (items) ) that is actually type DataTypeC the compiler suggest to cast C to type DataTypeC. and in getCount() I have also error suggesting to convert items to DataTypeC.

for example when I try to override getItem I getting mistakes, but when I create the same method with other name it compiles.

//Error - "The return type is incompatible with FragmentStatePagerAdapter.getItem(int)"
@Override
    public B getItem(int position) {
        return (B) data.get(position);
    }

//compiles
    public B getItemTest(int position) {
        return data.get(position);
    }

Any ideas how to fix it so it will be 100% generic?

ADDED: After your answer I changes it to support return of generic type :

   public class TypeA <T> {
    private T mData;

        public T getData() { return mData;; }
    }



    public class myAdapter <A,B,C> extends FragmentStatePagerAdapter {

        A myFragment;
        B data;
        C items;

        public myAdapter(FragmentManager fm, A fragment) {
                data = new SparseArray<B>();
                myFragment = fragment;
                items = myFragment.getData(); //Error - The method getData() is undefined for the type T
            }

}

I'm getting compile error... when I run it of course <T> and <C> are the same type.

Alex Opent
  • 111
  • 1
  • 13

2 Answers2

1

The first problem is that your non-generic code doesn't compile. You have a field DataTypeB data; which you then assign a SparseArray<DataTypeB> to. Also, setData doesn't compile at all.

Ignoring this... you have declared type parameters A, B, C and changed your instance field types, but you still try and assign DataTypaA to A myFragment in your constructor, and a SparseArray<DataTypeB> to B data. When you migrate code to generics you need to approach it like you would refactoring - one step at a time.

For instance, by diving in as you have now take a look at items = (C)myFragment.getData(). As fragment is still of type DataTypaA then presumably its getData() method doesn't return a generic type. Something is going to have to change.

You have a lot of work to do, so to repeat myself - treat this as a refactoring exercise and go step by step.

  1. Get your non-generic type to compile.
  2. Change myFragment to be a A myFragment and declare a type parameter <A extends DataTypaA. See what needs to be done to get this to compile before moving on.
  3. Now look at moving data to be SparseArray<B> data. Presumably this needs to a SparseArray<T> where T is a DataTypeB, or subtype thereof. That means you are going to need a wildcard-based bound on B. Something like B extends SparseArray<? extends DataTypeB>
  4. Now look at items. You know that it is returned from your new generic type variable A extends DataTypeA, so DataTypeA needs to be generic and have a generic type variable returned from getData. Let's say it is declared as DataTypeA<T extends DataTypeC> with public T getData() { ... }.

So now your type parameter sections change to:

class DataTypaA<T extends DataTypeC>
...
class myAdapter <A extends DataTypaA<C>, 
                 B extends SparseArray<? extends DataTypeB>, 
                 C extends DataTypeC> 
    extends FragmentStatePagerAdapter { 
    A myFragment;
    SparseArray<B> data;
    C items;
...

It's hard to go too far with this given the code you have posted - not all the classes and information is there, but this is the process you will have to follow.

For instance: you may not need to restrict C to DataTypeC. This then changes the type parameter sections on myAdapter and DataTypeA. My assumption about SparseArray<B> may also be incorrect - but your code doesn't compile at the moment so I can't tell. A final implementation may go something like:

class myAdapter <A extends DataTypaA<C>, 
                 B extends SparseArray<? extends DataTypeB, 
                                       ? extends DataTypeC>, 
                 C extends DataTypeC> 
    extends FragmentStatePagerAdapter {

    A myFragment;
    SparseArray<B, C> data;
    C items;

    public myAdapter(FragmentManager fm, A fragment) {
        data = new SparseArray<B, C>();
        myFragment = fragment;
        items =  myFragment.getData();
    }

    public B getItem(int position) {
        return data.get(position);
    }

    @Override
    public int getCount() {
        return items.getList().size();
    }   

    public void setData () {
        items = myFragment.getItems ();  //getItems return DataTypeC
        data.setTheData(items);
    }
}

For this I used the fake classes:

class DataTypaA<T extends DataTypeC> {
    public T getData() { return null; }
    public T getItems() { return null; }
}
class SparseArray<T, S> {
    public T get(int foo) { return null; }
    public void setTheData(S items){}
}
class DataTypeB {
}
class DataTypeC {
    // do NOT use the raw type List - just done to get your code to compile
    public List getList() { return null; }
}
abstract class FragmentStatePagerAdapter {
    public abstract int getCount();
}
class FragmentManager {
}
Andy Brown
  • 18,961
  • 3
  • 52
  • 62
  • Thank you for you answer. I fixed the syntax errors with the types, It's actually don't compile, and I write it for the big idea. I dont use ` `because it should work with types that not have something similar. My problems is when I initialise an parameter that should return the `` type like in `public B getItem(int position) { return data.get(position);` I get error while it override, If I change the name then it acceptable. also I have problem with `getCount()` - I extends it from Android class so I cant rewrite it with abstract as you suggested } – Alex Opent Feb 02 '15 at 18:54
  • 1
    @AlexOpent. Using `abstract` isn't necessary for `getCount` - I just had to fake those classes rather than load in the Android libraries. `extends` is optional - it depends on how you need to use the type parameters. – Andy Brown Feb 02 '15 at 20:31
  • 1
    @AlexOpent. For your other question about `public B getItem(int position) { return data.get(position);` you should ask a new question about overriding non-generic supertype methods. – Andy Brown Feb 02 '15 at 20:33
  • O.K I'll ask a new question about it is it something different? so basically what you saying is if I want that the next line will work : `items = myFragment.getItems ();` (where `items` is type `C`) I need to rewrite the getItems() so besides returning type C what it's doing today, It should also return generic type for that line? – Alex Opent Feb 02 '15 at 21:20
  • @AlexOpent. That's one option, there are others. It's definitely a question in itself. – Andy Brown Feb 02 '15 at 21:25
  • I tried what you sujested, to set return type of method to generic (not overriding non-generic supertype methods) but private of a class, but I getting error, see ADDED in my quesiotn – Alex Opent Feb 02 '15 at 22:46
  • @AlexOpent. I'm sorry - SO is strictly for one question per posted question. You will get better help if you post a new question with your new problem. There are lots of other people on SO who can help if you post a new question. – Andy Brown Feb 02 '15 at 22:49
0

When "genericizing" classes, you should replace all instances of genericized types by their generic: DataTypaA becomes A, etc.

You also need to make sure return types from methods you call on other objects (like fragment.getData()) are compatible with the generic. For that you can indicate the generic type extends a known class:

public class MyAdapter<A extends MyData,B extends MyData,...>

The Java Tutorial on generics is good for more information.

Matthieu
  • 2,736
  • 4
  • 57
  • 87