2

I'm writing a method that pretty straightforward and easy: I want it to get an object and work on it's starting and ending date. All of the object I want it to work on have dates to do stuff with, but of course eclipse won't allow me to write such a method. Can it be done? How?

Here's the frame I'm working on

public <T> void updateStuff(T stuffObject, Calendar startDate, Integer delta) {
    stuffObject.setStartDate(startDate);
    Calendar calendarEnd = Calendar.getInstance();
    calendarEnd = startDate;
    calendarEnd.add(Calendar.MONTH, delta);
    stuffObject.setEndDate(calendarEnd);
}
pedro
  • 417
  • 2
  • 7
  • 25
  • 2
    `` basicly mean that `T` will inherite `Object`, but that's all. You need to use `` to at least insure that the generic type will be one of `SomeClass` and then be able to use `stuffObject.methodOfSomeClass` – AxelH Oct 19 '17 at 08:38
  • 1
    You'll need to extend T, otherwise you can't do a lot with it. – achAmháin Oct 19 '17 at 08:39
  • If you are using a JDK 8+, what would you think about using the `TemporalAccessor` interface? (i.e. something along `T extends TemporalAccessor`) – Alexandre Dupriez Oct 19 '17 at 08:42
  • 2
    Isn't it overkill by the way, we could simply do `public void updateStuff(SomeInterface stuffObject, Calendar startDate, Integer delta)`. Since nothing is returned, there is no real interest in using generics type – AxelH Oct 19 '17 at 08:44
  • @AxelH Agreed - need to check what justifies using a generic type in this case. – Alexandre Dupriez Oct 19 '17 at 08:46
  • Well, justification is I actually didn't thought about using anything other then a generic type. Anyway, it's getting harder then I thought to do this because all the objects I was planning to work on are entities and they don't share any interface I can use. Therefore, it keeps showing me the "inferred type is not a valid substitute for[...] :( – pedro Oct 19 '17 at 09:01
  • It is still time to define an interface that would do it. Interface are not as tight as inheritance. Find a good solution and add the interface to the method, that won't break things (well, unless you add that interface to a method that don't have those methods ...) – AxelH Oct 19 '17 at 09:18

4 Answers4

3

A bit of topic but you could simply remove the genericity and directly use an interface as parameter.

Using OldCurmudgeon interface for the example :

interface Stuff {
    void setStartDate(Calendar startDate);
    void setEndDate(Calendar startDate);
}

public void updateStuff(Stuff stuffObject, Calendar startDate, Integer delta) {
    stuffObject.setStartDate(startDate);
    Calendar calendarEnd = Calendar.getInstance();
    calendarEnd = startDate;
    calendarEnd.add(Calendar.MONTH, delta);
    stuffObject.setEndDate(calendarEnd);
}

This is not necessary to use generic here and this give a method more verbose and, for non initiate, more obscure.

Generic could be useful if you returned the instance, to chained the method for example. Because with my example, you would need to cast the return instance.

AxelH
  • 14,325
  • 2
  • 25
  • 55
2

You have to tell Java that there is a setStartDate and a setEndDate method in the T objects.

interface Stuff {
    void setStartDate(Calendar startDate);
    void setEndDate(Calendar startDate);
}

public <T extends Stuff> void updateStuff(T stuffObject, Calendar startDate, Integer delta) {
    stuffObject.setStartDate(startDate);
    Calendar calendarEnd = Calendar.getInstance();
    calendarEnd = startDate;
    calendarEnd.add(Calendar.MONTH, delta);
    stuffObject.setEndDate(calendarEnd);
}

Then your objects need to implement this interface.

class SomeStuff implements Stuff {
    Calendar startDate;
    Calendar endDate;

    @Override
    public void setStartDate(Calendar startDate) {
        this.startDate = startDate;
    }

    @Override
    public void setEndDate(Calendar startDate) {
        this.endDate = endDate;
    }
}

Incidentally, the way you are working out the end date is dangerous, you are actually modifying the Calendar you are passed as a parameter.

    Calendar calendarEnd = Calendar.getInstance();
    // Throw away the new Calendar and point calendarEnd at the parameter.
    calendarEnd = startDate;
    // change the parameter.
    calendarEnd.add(Calendar.MONTH, delta);

See Defensive copy of Calendar for how to do this right. In particular this answer.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
1

The objects you want to pass to this method must implement a common interface or extend a common super class that contains the setStartDate() and setEndDate() methods.

Then you can define a type bound on T:

public <T extends SomeInterface> void updateStuff(T stuffObject, Calendar startDate, Integer delta) {
    stuffObject.setStartDate(startDate);
    Calendar calendarEnd = Calendar.getInstance();
    calendarEnd = startDate;
    calendarEnd.add(Calendar.MONTH, delta);
    stuffObject.setEndDate(calendarEnd);
}

where SomeInterface is the common interface.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • You can probably confirm my though in that specific example. Isn't it overkill ? Using the interface as the parameter type would be enough, It would be usefull if `T` was returned but here, I don't see the need for genericity. – AxelH Oct 19 '17 at 08:46
  • @AxelH That's a good point. My answer includes generics since the question includes generics, but I agree that in this specific example you can do without it. – Eran Oct 19 '17 at 08:48
  • Don't get me wrong, I was pointing the problem from the original question, not your answer ;) – AxelH Oct 19 '17 at 08:49
1

Your can defined the methods to be exposed by the generic type into an interface:

    public <T extends MyInterface> void updateStuff(T stuffObject, Calendar startDate, Integer delta) {
        stuffObject.setStartDate(startDate);
        Calendar calendarEnd = Calendar.getInstance();
        calendarEnd = startDate;
        calendarEnd.add(Calendar.MONTH, delta);
        stuffObject.setEndDate(calendarEnd);
    }

    interface MyInterface{
        void setStartDate(Calendar c);
        void setEndDate(Calendar c);
    }
Guillaume Barré
  • 4,168
  • 2
  • 27
  • 50