Kid101
Kid101

Reputation: 1470

How do I correctly use generics in this case?

Below is my Transformer Interface

public interface Transformer<BusinessObject, O extends State>
{
    public O transformToState(BusinessObject input);
}

This is one of my Transformer Impl

public class GoldTransformer implements Transformer<BusinessObject, Gold>
{
    @Override
    public Gold transformToState(BusinessObject input) {
        GoldBO goldbo= (GoldBO) input; // redundant casting line
        //do some transformation with BO to make it a state with some business logic
    }
}

Here is my another Transformer Impl

public class SilverTransformer implements Transformer<BusinessObject, Sliver> 
{
    @Override
    public Gold transformToState(BusinessObject input) {
        SilverBO goldbo= (SilverBO) input; // redundant casting line
        // again do some transformation with BO to make it a state with some business logic
    }
}

Both SilverBO and GoldBO Implements BusinessObject which is a marker interface. And Silver and Gold extend State. I really find the casting annoying and redundant is there a better way to use generics here? or a better pattern to use? I don't want to make any changes to state i.e. gold and silver.

Upvotes: 3

Views: 70

Answers (3)

Michael
Michael

Reputation: 44200

It looks like BusinessObject is a class, but you're also using the same identifier as a generic type parameter! In this code, BusinessObject does not refer to your class, it's a distinct identifier. If it's in a different package, you can verify this by verifying that it does not need to be imported.

interface Transformer<BusinessObject, O extends State>
{
    public O transformToState(BusinessObject input);
}

This is semantically identical to:

interface Transformer<FlyingSpaghettiMonster, O extends State>
{
    public O transformToState(FlyingSpaghettiMonster input);
}

Any decent IDE will colour generic type parameters differently to help you differentiate them. See below, the real type parameters are white, while the generic type parameters are dark green:

generic type parameter highlighting in IntelliJ IDEA

While you should change the generic type parameter to avoid confusion, all you technically need to do to remove the casting is change your transformer implementations like so:

public class GoldTransformer implements Transformer<GoldBO, Gold>
{                                                 // ^ changed
    @Override
    public Gold transformToState(GoldBO input) {
                                // ^ changed
    }
}

However, it's possible (or likely) that you'll also want to enforce that the first type parameter is a subclass of BusinessObject, otherwise it would be possible to create a Transformer<String, Gold>. To do this, change your interface to:

public interface Transformer<I extends BusinessObject, O extends State>
{
    public O transformToState(I input);
}

Where I is now the generic type parameter and BusinessObject is a real type which does refer to your class.

Upvotes: 2

Nikolas
Nikolas

Reputation: 44456

Make the input generic as well:

public interface Transformer<I extends BusinessObject, O extends State> {
    public O transformToState(I input);
}

Silver example:

public class SilverTransformer implements Transformer<SilverBO, Silver> {    

    @Override
    public Silver transformToState(SilverBO input) {
         return new Silver(input); // an example, perform the transformation...
    }   
}

Java 8 gives a shorter way

Alternatively, if you use Java 8 or higher, the Transformer<BusinessObject, O extends State> does exactly the same as java.util.Function<BusinessObject, O extends State>. Therefore the interface would be not needed. The usage is pretty neat:

Function<SilverBO, Silver> silverTransformer = (input -> new Silver(input));
Silver silver = silverTransformer.apply(silverBo);

Perform the transformation directly inside the lambda expression. If the transformation takes more lines, use the brackets {} and return.

Function<SilverBO, Silver> silverTransformer = (input -> {
    Silver output = // transformation ... 
    // ... more transformation ...
    return output;
});

Upvotes: 1

Mureinik
Mureinik

Reputation: 311853

You could generalize the interface on the input BusinessObject too:

public interface Transformer<I extends BusinessObject, O extends State> {
    public O transformToState(I input);
}

public class GoldTransformer implements Transformer<GoldBO, Gold> {    
    @Override
    public Gold transformToState(GoldBO input) {
        // Code...
    }
}

Upvotes: 5

Related Questions