简体   繁体   中英

Convert custom adapter to use generics in Java

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.

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> 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 {
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM