9

Say I have these two ArrayLists:

ArrayList<Book> book = new ArrayList<>();
ArrayList<Journal> journal = new ArrayList<>();

Book and Journal are two different classes.

Both Book and Journal objects have a getYear() method. I want to make a method that passes in an unknown ArrayList type and compares a passed in year to an object in the list. The following code is in main:

public static void fooBar(int year, ArrayList<?> list)
{
    if(list.get(0).getYear() == year) // does not work!
    {
    }
}

If an unknown type is passed into the method, I cannot use that object's methods (getYear()). How can I do this without making two methods that do the same thing (one for Book and one for Journal)?

3
  • 4
    Do Book and Journal share a superclass or interface that defines getYear()? Commented Oct 13, 2015 at 15:54
  • My method signature was wrong, I edited the question. Commented Oct 13, 2015 at 15:56
  • 1
    What @JonK asked... If it doesn't you should make one and that would solve your problem Commented Oct 13, 2015 at 15:56

4 Answers 4

13

You can make an interface (if it doesn't already exist) (possibly named HasYear) that declares the getYear() method, and have Book and Journal implement it.

Then you can have your fooBar method take an ArrayList of some type parameter that is a HasYear.

public static void fooBar(int year, ArrayList<? extends HasYear> list)

The ? extends is due to the fact it's a consumer and not a producer.

Sign up to request clarification or add additional context in comments.

7 Comments

I am learning Java in school and I haven't learned what an interface is. Why can't Java just infer the type of list I send into the method so I can use that object's methods?
With a List<?> parameter, you are allowed to pass a list of anything, e.g. List<Integer>, List<Foo>, List<?>, etc., which won't necessarily have a getYear() method. The compiler won't allow you to call a method it can't guarantee exists.
Your list is specified in the method as List<?>, so it could contain anything.
if I call the method like this: fooBar(2015, bookList), and bookList is an ArrayList of type Book, then shouldn't I be able to use Book's methods in fooBar?
@MortalMan it sounds like you want to use a language other than Java, like Ruby, Scala or Javascript.
|
4

Like JonK said, if they share a superclass or interface that defines getYear() then this will be made easier. Actually, the have to have either of those two relationships to make your proposed relationship.

interface HasYear {
    public int getYear();
}

Book.java

public class Book implements HasYear{
    //...
    public int getYear(){/*YOUR IMPLEMENTATION*/}
}

Journal.java

public class Journal implements HasYear{
    //...
    public int getYear(){/*YOUR IMPLEMENTATION*/}
}

Now, you can create and use your ArrayList like this:

public static void fooBar(int year, ArrayList<? extends HasYear> list){
    if(list.get(0).getYear() == year){
        //yay
    }
}

Comments

3

Have both Book and Journal extend a common superclass (Publication perhaps?) or implement a common interface which defines the getYear() method:

public abstract class Publication {
    private int year;

    public int getYear() {
        return year;
    }

    public Publication(int year) {
        this.year = year;
    }
}

If you then change your Book and Journal class declarations and constructors:

public class Book extends Publication {

     public Book(int year, ...) {
         super(year);
         ...
     }
public class Journal extends Publication {

     public Journal(int year, ...) {
         super(year);
         ...
     }

You could then pass around a list of Publication objects:

public void fooBar(int year, List<? extends Publication> list) { ... }

Because it's Publication that defines the getYear() method, you can freely use it on objects contained within list.

4 Comments

As other answers have demonstrated, you can also use an interface to accomplish the same thing in much the same way.
using List<Publication> as argument allows you to call list.add(new Book()) regardless of the actual type of the content of list, which is very dangerous. ? extends Publication prevents that.
@njzk2 I suppose it really depends on what you're doing with the list, but yes, point taken. I'll update the answer.
@njzk2 Umm... no: When the type is List<Publication>, then you can only pass in a List<Publication>, and then you can add a Book - that's fine. But as the list is only read here, allowing a List<? extends Publication> increases the flexibility (it can be called with a List<Publication>, but also with a List<Book>), and is more idiomatic in terms of PECS
1

Everyone discussed about the right way of doing it, such as associating your object with a super class containing your desired method, but if you want some fancy implementation without using super class, you can try lambda expression:

ArrayList<Book> book = new ArrayList<>();
book.stream().limit(1).filter(s->s.getYear()==year).forEach(yourcode)

I know this is not what you wanted but you have right solution from other comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.