0

I'm writing a program which contains an abstract class of 'Book', and I have two classes ('LearnBook' and 'ReadingBook') which inherit from 'Book'.

Book:

Public abstract class Book {
protected String name;
protected String author;

LearningBook:

public class LearningBook extends Book {
private String subject;

ReadingBook:

public class ReadingBook extends Book {
private int numberOfPages;

At the main class I have Book array which can include any instance of Book.

I want to add a method which checks if two Book objects are exactly the same, to prevent duplicating in the Book array. it looks like this:

public boolean sameBookCheck(Book book1, Book book2)

So my first idea was to write an isEqual() method in the Book class, which checks if the "name" and the "author" are equals. But then I need to check if it's a learning book or reading book so I could know if I need to compare the "subject" value or the "numberOfPage" value.

I have no idea how to do it and I'd appreciate your help.

Jon
  • 1
  • 1
  • 3
    I'd firstly suggest reading on object equality in Java (see [here](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)) for API, but you probably want to broaden the search to concepts/tutorials). Also note that if you correctly override `hashCode` and `equals`, you can use `Set`s to automatically maintain duplicate-free collections. – Mena Nov 26 '18 at 16:35
  • You can add `obj1.getClass().equals(obj2.getClass())` to the method that checks the books for equality. This condition is true only if both books are instances of the same subclass. May be a [dupe of this](https://stackoverflow.com/questions/10162802/see-if-two-object-have-the-same-type). – DYZ Nov 26 '18 at 16:39
  • Try using something like: if(book instanceof ReadingBook ) condition in your eqauls method, then cast it to the appropriate type if its true. – Yan.F Nov 26 '18 at 16:40

5 Answers5

2

You can use the following design:

  1. In Book abstract class have an equals() function and check whether the other object is of type Book and have same values in all fields.
  2. In LearningBook and ReadingBook have equals() function which first checks whether the other object is of same class, then call Book's equals() function, checking the fields of abstract class, and then check whether field(s) of current class have same values or not.

Have a look at the code:

abstract class Book {

    protected String name;

    protected String author;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((author == null) ? 0 : author.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof Book))
            return false;
        Book other = (Book) obj;
        if (author == null) {
            if (other.author != null)
                return false;
        } else if (!author.equals(other.author))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

}


class LearningBook extends Book{

    private String subject;

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + ((subject == null) ? 0 : subject.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!super.equals(obj))
            return false;
        if (!(obj instanceof LearningBook))
            return false;
        LearningBook other = (LearningBook) obj;
        if (subject == null) {
            if (other.subject != null)
                return false;
        } else if (!subject.equals(other.subject))
            return false;
        return true;
    }

}


class ReadingBook extends Book{

    private int numberOfPages;

    public int getNumberOfPages() {
        return numberOfPages;
    }

    public void setNumberOfPages(int numberOfPages) {
        this.numberOfPages = numberOfPages;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + numberOfPages;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!super.equals(obj))
            return false;
        if (!(obj instanceof ReadingBook))
            return false;
        ReadingBook other = (ReadingBook) obj;
        if (numberOfPages != other.numberOfPages)
            return false;
        return true;
    }

}


public class Runner {

    public static void main(String[] args) {

        Book learningBook = new LearningBook();
        learningBook.setAuthor("auth");
        learningBook.setName("sci");

        Book learningBook2 = new LearningBook();
        learningBook2.setAuthor("auth");
        learningBook2.setName("sci");


        Book readingBook = new ReadingBook();
        readingBook.setAuthor("auth");
        readingBook.setName("sci");

        //returns false
        System.out.println(learningBook.equals(readingBook) );

        //returns true
        System.out.println(learningBook.equals(learningBook2) );
    }

}
Rishabh Agarwal
  • 1,988
  • 1
  • 16
  • 33
1

You can use instanceof method to compare the type of the Object. To check if it is a type of LearningBook or ReadingBook example

Answer for your comment,

Lets say when you check the two instance it says they are different, then there is no issue you can return false. But if the instances are also same then you can check it with something like this after that

if (both instances are same) {
  if (yourObjectIs instanceof LearningBook) {
    you can check the two values of LearningBook here and return true if the are equals
  } else {
    you can check the two values of ReadingBook here and return true if the are equals
  }
}
Sandeepa
  • 3,457
  • 5
  • 25
  • 41
  • But I also need to compare between the specific variable of the subclasses ('subject' for LearningBook and 'numberOfPages' for ReadingBook). – Jon Nov 26 '18 at 16:52
  • You are writing a method for this correct? Then if the type of those two objects are not equals you can return fasle. Else if the types are same, you can extend your methods to check the specific variables also. I'll add a example to my answer – Sandeepa Nov 26 '18 at 17:00
1

Write an equals-implementation for each of the three classes. Every implementation is only responsible for its own fields.

The equals-implementations from the sub-classes ReadingBook and LearningBook should somewhere call super.equals() - the equals-implementation of Book.

Fabian Barney
  • 14,219
  • 5
  • 40
  • 60
  • I've tried it, but since the "sameBookCheck" method gets 'Book' objects, the isEqual method runs from the 'Book' class and not from the LearningBook or ReadingBook – Jon Nov 26 '18 at 16:49
1

You can ask the book instance for its class and check class equality.

book1.getClass().equals(book2.getClass())
John Camerin
  • 677
  • 5
  • 15
1

As it was mentioned you should overwrite equals(Object object) method. In your example you can do it like this:

    public abstract class Book{
    @NonNull protected String name;
    @NonNull protected String author;

    public Book(String name, String author) {
        this.name = name;
        this.author = author;
    }

    @Override
    public boolean equals(Object object) {
        if (object instanceof Book) {
            var book = (Book) object;
            return this.name.equals(book.name) && this.author.equals(book.author);
        } else
            return false;
    }
}

public class LearningBook extends Book{
    @NonNull private String subject;

    public LearningBook(String name, String author,String subject) {
        super(name, author);
        this.subject = subject;
    }

    @Override
    public boolean equals(Object object) {
        if (object instanceof LearningBook) {
            var book = (LearningBook) object;
            return this.subject.equals(book.subject) && super.equals(book);
        } else
            return false;
    }
}

public class ReadingBook extends Book{
    @NonNull private int numberOfPages;

    public ReadingBook(String name, String author,int numberOfPages) {
        super(name, author);
        this.numberOfPages = numberOfPages;
    }

    @Override
    public boolean equals(Object object) {
        if (object instanceof ReadingBook) {
            var book = (ReadingBook) object;
            return super.equals(book) && this.numberOfPages == book.numberOfPages;
        } else
            return false;
    }
}

I've used @NonNull annotation to avoid NPE in equals method.

jker
  • 465
  • 3
  • 13