3

I get this compile error "required T, found UseCaseTest" when I try to return INSTANCE at the method keepStateBetweenTests of the class UseCaseTest. I know I can easily fix it casting to T, but I want to do it right.

Plus, I don't know why the line

if (INSTANCE == null) INSTANCE = getInstance(activityTestRule);

can compile, given the previous error.

I supply two more classes to provide certain context.

UseCaseTest

public abstract class UseCaseTest {
    private static UseCaseTest INSTANCE;

    public abstract static class Builder<T extends UseCaseTest> {
        private final ActivityTestRule activityTestRule;

        protected Builder(ActivityTestRule activityTestRule) {
            this.activityTestRule = activityTestRule;
        }

        public T keepStateBetweenTests() {
            if (INSTANCE == null) INSTANCE = getInstance(activityTestRule); 
            return INSTANCE; //compile error: required T, found UseCaseTest
        }

        public T releaseStateBetweenTests() {
            return getInstance(activityTestRule);
        }

        protected abstract T getInstance(ActivityTestRule activityTestRule);
    }

    private final ActivityTestRule activityTestRule;

    public UseCaseTest(ActivityTestRule activityTestRule) {
        this.activityTestRule = activityTestRule;
    }
}

SessionUseCaseTest

public final class SessionUseCaseTest extends UseCaseTest {

    public static Builder<SessionUseCaseTest> with(ActivityTestRule activityTestRule) {
        return new Builder<SessionUseCaseTest> (activityTestRule) {
            @Override protected SessionUseCaseTest getInstance(ActivityTestRule activityTestRule) {
                return new SessionUseCaseTest(activityTestRule);
            }
        };
    }

    private SessionUseCaseTest(ActivityTestRule activityTestRule) {
        super(activityTestRule);
    }


    public void signUp() {}

    public void logout() {}
}

SessionTest

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SessionTest extends BaseTest {

    @Test public void _1_SingUp() {
        SessionUseCaseTest.with(mActivityRule)
                .keepStateBetweenTests()
                .signUp();
    }

    @Test public void _2_Logout() {
        SessionUseCaseTest.with(mActivityRule)
                .keepStateBetweenTests()
                .logout();
    }

}
Víctor Albertos
  • 8,093
  • 5
  • 43
  • 71

2 Answers2

1

You need to make your class aware of type T i.e.

public abstract class UseCaseTest <T> {

You can do like below:-

    class UseCaseTest<T> {
        final Class<T> useCaseTestType;

        public UseCaseTest(Class<T> useCaseTestType) {
            this.useCaseTestType = useCaseTestType;
        }

//Then write your methods which are generic
}
Amit Bhati
  • 5,569
  • 1
  • 24
  • 45
  • You can't hold a generic reference in a static reference, which is the whole point of this class. Plus, I don't know how it can fix the problem. – Víctor Albertos Sep 04 '15 at 17:56
1

The topic of static method/fields in conjunction with generics is very well described in this post

Nevertheless, it would seem a simple casting makes the compiler happy while allowing to keep some level of generics in place:

public T keepStateBetweenTests() {
    if (INSTANCE == null)
        INSTANCE = getInstance(activityTestRule); 

    return (T)INSTANCE;
}

UPDATE

Part of your question was why this can compile although there is the error:

if (INSTANCE == null) INSTANCE = getInstance(activityTestRule);

To my understanding, the compiler knows the INSTANCE itself is actually UseCaseTest, which fulfills the restriction defined on the Builder class: <T extends UseCaseTest>

Community
  • 1
  • 1
Sva.Mu
  • 1,141
  • 1
  • 14
  • 24
  • 1
    Oh, now I noticed you do not want to cast to T. Anyway, it is the only solution I could think of now. Also, I'd say in this case it IS the correct way to do it as you already ensure T to be of the correct type by using `Builder` – Sva.Mu Sep 04 '15 at 18:50
  • I forgot to answer one of your questions, please re-check the updated answer. – Sva.Mu Sep 04 '15 at 19:05
  • So, INSTANCE accepts the value retuned by getInstance(activityTestRule) because this T value means any class derived from UseCaseTest, and because UseCaseTest is the base class itself, this is ok. But keepStateBetweenTests returns also T, which means any class derived from UseCaseTest (again), and the compiler already knew that. I don’t really see the difference, really. Anyway, I’ll accept your answer if nobody presents a solution without involving casting. Thanks! – Víctor Albertos Sep 04 '15 at 19:17
  • @VíctorAlbertos I came across this question once again and can't see any further updates here - have you been able to find a solution that doesn't require casting? I'd be glad to learn something new. – Sva.Mu Oct 02 '15 at 17:45
  • I don't. I just did the cast ;) – Víctor Albertos Oct 03 '15 at 02:19
  • 1
    @VíctorAlbertos that's a pity, I was seriously hoping to see a "cleaner solution" – Sva.Mu Oct 03 '15 at 06:14