Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining a a generic method for two subclasses that implement the same interface

Tags:

java

generics

set

I'm building a "Contact Manager" in Java.

I have a superclass called "Contact which has two base classes; PersonalContact and BusinessContact.

I have an interface called Event, which is implemented by classes Birthday and Meeting. (Birthday contains one DateTime object while Meeting has two for start and end time).

PersonalContact holds a TreeSet of Birthdays and BusinessContact holds a set of Meetings.

Now, in the superclass Contact, I want to create an abstract method called "getEventsWithinPeriod()" which will return a TreeSet of all birthdays and/or meetings within a given time span.

The problem is, I don't know how to tell the abstract method, and then the base class methods what to return.

For example, this is the code I used in Contact;

public abstract Set<Event> getEventsWithinPeriod(DateTime start, DateTime end);

And in PersonalContact;

public Set<Birthday> getEventsWithinPeriod(DateTime start, DateTime end){

      Set<Birthday> birthdaysThatAreWithin = new TreeSet<Birthday>();
      //CODE
      return birthdaysThatAreWithin;

However, in the compiler, I'm getting an error on Set<Birthday> saying;

"The return type is incompatible with Contact.getEventsWithinPeriod(DateTime, DateTime)"

What are the proper terms and returns I should be using? Why is my current attempt wrong?

like image 450
CodyBugstein Avatar asked Nov 02 '12 16:11

CodyBugstein


People also ask

What are generic methods generic methods are the methods defined in a generic class?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

Can a class implement different instantiations of the same generic interface Java?

This is not possible.

Can a class implement the same interface twice?

A class can implement multiple interfaces and many classes can implement the same interface. Final method can't be overridden. Thus, an abstract function can't be final.

Which method is used to define a generic method?

Which of these is an correct way of defining generic method? Explanation: The syntax for a generic method includes a type parameter, inside angle brackets, and appears before the method's return type. For static generic methods, the type parameter section must appear before the method's return type.


2 Answers

You need to use generic Types

public abstract class Contact<T extends Event> {
    public abstract Set<T> getEventsWithinPeriod(Date start, Date end);
}
public class BirthDay extends Contact<BirthDay> implements Event {

    @Override
    public Set<BirthDay> getEventsWithinPeriod(Date start, Date end) {
        return null;
    }
}
like image 149
Amit Deshpande Avatar answered Nov 13 '22 06:11

Amit Deshpande


You have 3 solutions.

Solution 1

First, you can make your classes generic, like so:

public abstract class Contact<E extends Event> {
    // ...

    public abstract Set<E> getEventsWithinPeriod(DateTime start, DateTime end);
}

And then in your concrete implementation:

public class PersonalContact extends Contact<Birthday> {

    public Set<Birthday> getEventsWithinPeriod(DateTime start, DateTime end) { ... }
}

This is the best solution, but you have some alternatives.

Solution 2

You can change the type of your birthdaysThatAreWithin field:

Set<Event> birthdaysThatAreWithin = new TreeSet<Event>();

as well as change the method signature:

public Set<Event> getEventsWithinPeriod(DateTime start, DateTime end) {

and return it like that. This restricts you because you can't use the events as Birthday instances anymore.

Solution 3

You could also change your method signature (in both your abstract and concrete class) to this:

public Set<? extends Event> getEventsWithinPeriod(DateTime start, DateTime end)

and not change anything else. This has the same problem as solution 2, you won't be able to use the events as Birthday instances without casting them.

Edit: the downsides to 2 and 3 are that they will require casting. For example:

PersonalContact contact = ... ;
Set<Event> events = personalContact.getEventsWithinPeriod(start, end);
// I know all the events are birthdays, but I still have to do this:
for (Event event : events) {
    if (event instanceof Birthday) {
        Birthday birthday = (Birthday) event;
        // Do stuff with birthday
    } // else maybe log some error or something
}

With the first solution, you'd have this:

PersonalContact contact = ... ;
Set<Birthday> birthdays = personalContact.getEventsWithinPeriod(start, end);
for (Birthday birthday : birthdays) {
    // Do stuff with birthday
}

The code looks cleaner and runs better because you don't have to do instanceof checks to make sure you don't get a ClassCastException. You can also have stuff like this:

public static void processBirthdaysFor(Contact<Birthday> birthdayContact, DateTime start, DateTime end) {
    Set<Birthday> birthdays = personalContact.getEventsWithinPeriod(start, end);
    for (Birthday birthday : birthdays) {
        // Do stuff with birthday
    }
}

And if you ever have another implementation of Contact that has Birthday events, you can pass them to that processBirthdaysFor method without making any changes.

However, if you only need the events and you don't care what the types are in the code calling your Contact.getEventsWithinPeriod, then solutions 2 and 3 are definitely your best bets. I'd personally just use solution 2 if this was the situation.

like image 20
Brian Avatar answered Nov 13 '22 06:11

Brian