Using Java Generics in Abstract Base Class [duplicate] - java

This question already has answers here:
how to inherit Constructor from super class to sub class
(6 answers)
Closed 7 years ago.
I have created an abstract base class BaseModelDao with three constructors. When I create a class SubscriberScoreDao that extends BaseModelDao I have to redefine all three constructors in the subclass in order to avoid compile time errors. Is there a way to take advantage of the constructors I have defined in my BaseModelDao without having to reimplement the same logic in every subclass?
BaseModelDao
public abstract class BaseModelDao<T extends Model> {
private static final String TAG = BaseModelDao.class.getSimpleName();
private List<T> mModelList;
protected BaseModelDao() {
mModelList = new ArrayList<>();
}
protected BaseModelDao(Response<T>[] responseArray) {
mModelList = fromResponseArray(responseArray);
}
protected BaseModelDao(Response<T> response) {
mModelList = fromResponse(response);
}
public List<T> getModelList() {
return mModelList;
}
public abstract Class<T> getModelClass();
private List<T> fromResponse(Response<T> response) {
List<T> responseList = response.getResultData();
return responseList;
}
public List<T> fromResponseArray(Response<T>[] responseArray) {
return fromResponse(getResponseObjectFromArray(responseArray));
}
// more helper methods...
}
SubscriberScoreDao
public class SubscriberScoreDao extends BaseModelDao<SubscriberScore> {
public static final String TAG = SubscriberScoreDao.class.getSimpleName();
public SubscriberScoreDao(){
super();
}
public SubscriberScoreDao(Response<SubscriberScore>[] responseArray) {
super(responseArray);
}
public SubscriberScoreDao(Response<SubscriberScore> responseArray) {
super(responseArray);
}
#Override
public Class<SubscriberScore> getModelClass() {
return SubscriberScore.class;
}
}
The constructors shown above are the ones I am trying to eliminate. When I want to use the SubscriberScoreDao in code it looks like this.
LendingRestClient.getInstance().getSubscriberScoring(new Callback<Response<SubscriberScore>[]>() {
#Override
public void success(Response<SubscriberScore>[] responseArray, retrofit.client.Response response) {
mSubscriberScoreDao = new SubscriberScoreDao(responseArray);
}
#Override
public void failure(RetrofitError error) {
}
});
If the three constructors that call super() are not defined in the SubscriberScoreDao then the code throws a compile time error at this line:
mSubscriberScoreDao = new SubscriberScoreDao(responseArray);
Error:
Is there a way to not define the constructors in every subclass and avoid this error?

You could declare the constructor (in the base class) with a vararg:
class Super<T> {
private List<T> responses;
public Super(Response<T>...responses) {
this.responses = Arrays.asList(responses);
}
}
Your subclass would only have to declare 1 constructor, which takes care of the functionality of all 3 of the constructors you have.
class Sub extends Super<SubscriberScore> {
public Sub(Response<SubscriberScore>...responses) {
super(responses);
}
}
You can now instantiate Sub as:
new Sub();
new Sub(new Response<SubscriberScore>());
new Sub(new Response<SubscriberScore>[] {
});

Related

Java, Inheritance, Generics - using parameters of subtypes in template method implementations

I am building an application that is generating PDF documents for sales orders and sales invoices. For simplicity I have exluded redundant logic and fields.
Here is my class structure:
public class SalesEntity {
public String name;
public String createdDate;
}
public class SalesOrder extends SalesEntity {
}
public class SalesInvoice extends SalesEntity {
public String invoiceSpecificField;
}
and similar scturcure for wrapper and list items:
public class SalesEntityItem {
public String name;
public String price;
}
public class SalesOrderItem extends SalesEntityItem {
}
public class SalesInvoiceItem extends SalesEntityItem {
public String invoiceItemSpecificField;
}
public class SalesEntityResponse {
public SalesEntity salesEntity;
public List<SalesEntityItem> salesEntityItems;
}
and here is first part of the problem.
public class SalesOrderEntityResponse extends SalesEntityResponse {
// public SalesOrder salesEntity; <-- say somehow to java that in this subclass the property type should also be subclass
// public List<SalesOrderItem> salesEntityItems;
}
For building mechanism I am using template method:
public class PDFBuilder extends AbstractPDFBuilder {
protected void buildPdfDocument(Map<String, Object> model /*...*/) throws Exception {
/*...*/
addEntityNumber(document, salesEntityResponse);
addItems(document, salesEntityResponse);
}
}
public class OrderPDFBuilder extends PDFBuilder {
/*...*/
#Override
protected void addEntityNumber(Document document, SalesEntityResponse entityResponse) throws DocumentException {
/*...*/
PdfPTable documentNameTable = new PdfPTable(1);
Phrase documentNamePhrase = new Phrase(entityResponse.labels.account_number, timesFont);
PdfPCell documentNameCell = new PdfPCell(documentNamePhrase);
documentNameTable.addCell(documentNameCell);
document.add(documentNameTable);
}
#Override
protected void addItems(Document document, SalesEntityResponse entityResponse) throws DocumentException {
/*...*/
for (SalesEntityItem salesEntityItem : entityResponse.salesEntityItems) {
/* Items adding specific logic */
}
}
}
And if first part of somwhow solveable, here comes main part:
Question: How can I make specific template method implementations (OrderPDFBuilder, InvoicePDFBuilder)
receive parameters of subtype SalesEntityResponse (SalesOrderResponse and SalesInvoiceResponse) respectively? So that in specific implementations I can use specific fields of those entities. Does it make sense? I assume here is something related to bounded types, but I am not sure how to use it properly.
As you've established generics are how you achieve this. So firstly you'll need to stick your generics decleration on the SalesEntityResponse:
public class SalesEntityResponse<T extends SalesEntity, U extends SalesEntityItem> {
public T salesEntity;
public List<U> salesEntityItems;
}
Then in the declaration of your subtypes you tell it what concrete types they hold:
public class SalesOrderEntityResponse extends SalesEntityResponse<SalesOrder, SalesOrderItem> {
}
To make the "magic" work in your addItems method you'll have to also have to add generics to which ever class/interface declares the addItems method, this is presumably AbstractPDFBuilder? So something like:
public abstract class AbstractPDFBuilder<T extends SalesEntity, U extends SalesEntityItem, V extends SalesEntityResponse<T, U>> {
protected abstract void addItems(Document document, V entityResponse) throws DocumentException;
}
}
And then your concrete PDFBuilder types need to supply the relavant generics:
public class SalesOrderPDFBuilder extends AbstractPDFBuilder<SalesOrder, SalesOrderItem, SalesOrderEntityResponse> {
protected void addItems(Document document, SalesOrderEntityResponse entityResponse) {
}
}

Generically providing a setter for a Decorated object that is stored in an array

I'm probably going about this in the most complicated way, but I'm hoping what I'm trying to do makes sense here.
Suppose I have some set of unrelated, generated classes and I want to Decorate them to create some kind of common API. So something like:
public abstract class GeneratedDecorator<T> {
private T generated;
public T getGenerated() { return generated; }
public void setGenerated(T generated) { this.generated = generated; }
public abstract String getString();
public static class ClassA extends GeneratedDecorator<GeneratedClassA> {
#Override
public String getString() { return getGenerated().getThisString(); }
}
public static class ClassB extends GeneratedDecorator<GeneratedClassB> {
#Override
public String getString() { return getGenerated().getADifferentString(); }
}
}
Now, to use this new fancy class I just say:
GeneratedDecorator.ClassA a = new GeneratedDecorator.ClassA();
a.setGenerated(myGeneratedInstanceA);
a.getString();
Ok so far so-so ... but now I want to manage an array of these Decorators.
So let's try:
public abstract class DecoratorBundle<T extends GeneratedDecorator> {
private static final int MAX_ROWS = 10;
private T[] bundle;
DecoratorBundle() { bundle = createBundle(); }
public String getString(int index) { return bundle[index].getString(); }
public void setRow(??? generated, int index ) {
//check index of bundle, if null create a new instance of appropriate type and set bundle[index] = new instance
//call setGenerated on instance at index
}
protected abstract T[] createBundle();
public static class ClassA extends DecoratorBundle<GeneratedDecorator.ClassA> {
#Override
protected GeneratedDecorator.ClassA[] createBundle() {
return new GeneratedDecorator.ClassA[MAX_ROWS];
}
}
public static class ClassB extends DecoratorBundle<GeneratedDecorator.ClassB> {
#Override
protected GeneratedDecorator.ClassB[] createBundle() {
return new GeneratedDecorator.ClassB[MAX_ROWS];
}
}
}
Here's where I'm stuck ... I want this DecoratorBundle to have a setRow(??? generated, int index) where the parameter is of the GeneratedDecorator's type (i.e, GeneratedClassA or GeneratedClassB). Seems like type erasure will probably make this impossible, but it would be really nice to have this DecoratorBundle class to completely manage it's bundle array. It currently is able to instantiate the array, but I want some way for it to create a new GeneratedDecorator-type and assign it in a setRow method.
If I'm going about this completely wrong then I would love to hear another idea.

Error when trying to return a value with generics

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();
}
}
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>
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
}

Creating an object of the same class as the parameterized type [duplicate]

This question already has answers here:
Instantiating a generic class in Java [duplicate]
(10 answers)
Closed 8 years ago.
Here's the code:
package Fabrika.Trake;
import Vozila.*;
public class Traka<T extends Vozilo> extends Thread
{
protected int vrijemeRada;
...
#Override
public void run()
{
while(true)
{
//I want to create the object here
}
}
}
Now, let's say I have a class called Auto which extends Vozilo.
I'd like to know if there is a way of constructing an object of type T without using reflection. Something like:
T object = new T();
This, of course, is completely incorrect, but it just serves as an illustration of what I'd like to do. :)
Use a type token.
public class Traka<T extends Vozilo> extends Thread
{
private Class<T> type;
protected int vrijemeRada;
...
public Traka(Class<T> type) {
this.type = type;
}
#Override
public void run()
{
while(true)
{
//I want to create the object here
T object = type.newInstance();
}
}
}
This works - of course - only with constructors that have a known number of arguments. My example uses a no-arg constructor.
And: Do not extend Thread! Implement Runnable instead. And then uses those runnables, wherever you need them.
You can't do this with just the generic type T.
To create an instance you need a Class instance.
E.g.:
public class Traka<T extends Vozilo> extends Thread
{
protected int vrijemeRada;
...
private Class<T> type;
public Traka(Class<T> type) {
this.type = type;
}
#Override
public void run()
{
while(true)
{
T newInstance = type.newInstace(); // this uses the default constructor
}
}
}
Usage:
Traka<Auto> autoTraka = new Traka(Auto.class);
The problem is that the generic type information T is erased at runtime. Therefore you need a Class instance to represent the type at runtime.
There is a special pattern for this: pass Class instance to the constructor of Traka, store it in a private variable, and the use that class to instantiate new objects, like this:
public class Traka<T extends Vozilo> extends Thread {
private Class<T> theClass;
public Traka(Class<T> theClass) {
this.theClass = theClass;
}
...
#Override
public void run() {
while(true) {
T inst = theClass.newInstance();
}
}
}
Note: In case you were wondering why Class became parameterized in Java 5, the pattern above is the answer.

Wildcard constructor in a collection

In libgdx you can create a pool easily by using the abstract class Pool.
Since it is an abstract class you need to override it's methods anonymously when constructing it like this:
public Pool<String> pool = new Pool<String>() {
#Override
protected String newObject() {
return new String();
}};
What I'm trying to do now is put a wildcard for the generic type T which extends a abstract BaseClass like this:
protected static Pool<? extends GameCommand> commandPool = new Pool<? extends GameCommand>() {
#Override
protected <T extends GameCommand> newObject() {
return new T();
}
};
But it won't work. How can I get it?
What kind of type will I have to insert when overriding newObject()?
EDIT:
Pool references it's abstract method like this:
abstract public class Pool<T> {
(...)
abstract protected T newObject ();
(...)
}
If you make a small change like making a factory method, it can be done fairly simply. As long as you can assure that all subclasses of GameCommand have a default constructor. (Although, you could get around that with Objenesis).
Just do the following:
public static <T extends GameCommand> Pool<T> createCommandPool(final Class<T> clazz) {
return new Pool<T>() {
#Override protected T newObject() {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e); // cause there isn't really much else you can do.
}
}
};
}
public static void main(String[] args) {
Pool<? extends GameCommand> gcPool = createCommandPool(GameCommand.class);
// MyCommand extends GameCommand
Pool<? extends GameCommand> mcPool = createCommandPool(MyCommand.class);
}

Categories