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?
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.
This is not possible.
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 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.
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;
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With