1

In my case, I have two Objects, Book and User on a library control program, and they have some methods that do the same thing, like:

void change_name(String name) {
    this.name = name;
}

But as I am learning Java, I would like to know some way to not have to repeat code between objects.

I know that I can do inheritance with them but I don't think that's a elegant solution, as the two objects are very different in another ways.

I would like to know your solutions.

  • 2
    Not really besides having them extend something like `NamedObject` class, but that would be a weird thing to do. You'll have to accept that even though both books and users have names, they don't share the code for it. The method should also be called `setName(String name)` and you'll have a proper [setter](https://stackoverflow.com/questions/2036970/how-do-getters-and-setters-work). – Kayaman Mar 09 '21 at 15:49
  • 5
    *"I know that I can do inheritance with them but I don't think that's a elegant solution, as the two objects are very different in another ways."* - This is good intuition. A very common trap for beginners is to take DRY (don't repeat yourself) to the extreme. Just because code looks similar does not necessarily mean that it should be abstracted. – 0x5453 Mar 09 '21 at 15:49
  • 1
    "I don't think that's a elegant solution, as the two objects are very different in another ways" you're damn right about that. But sticking to your example, it doesn't make sense to generalize a method like that. If you have more complex methods that are used in more than one place, it's probably the moment to have that behavior in a whole different class that your other classes are going to use. – Federico klez Culloca Mar 09 '21 at 15:49
  • 2
    A 1-line setter method as you posted above really shouldn't make you worry about "duplicate code". If that method ever grows to multiple lines that are present duplicated multiple times in the code then you can think about how you can reduce reduncancies, but a single line like above is really no reason to worry. – OH GOD SPIDERS Mar 09 '21 at 15:49
  • 1
    If repeating the code is your main concern, you can use [Lombok](https://projectlombok.org/), which does that for you (you'll still have to repeat an annotation though). – Maroun Mar 09 '21 at 15:50
  • Since Java doesn't have mixin composition, you can't avoid the code duplication. You can explore Scala, which can support this using traits. – HRJ Mar 09 '21 at 16:37

2 Answers2

0

Different objects with similar methods are just different objects with similar methods. There's no reason to try to unify the behavior between a Book and a User if they're fundamentally different, even if they do some slightly similar things.

Note that changing the name of a Book is different than changing the name of a User, where I wouldn't expect the name of a book to change much at all, the name of a user may change very often, so I would question the value on a Book object to begin with. That may be another point of contention; don't put methods on objects that don't make a whole lot of practical sense to begin with.

Makoto
  • 104,088
  • 27
  • 192
  • 230
0

Apart from the points discussed in other answers and comments, which state that for your specific case, it is fine to have independent methods, nevertheless there may be cases where you have a one or more methods which are implemented by independent classes, but are common behavior of these classes. For this purpose, an interface is what Java provides: If several classes have similar behavior, but independent inheritance hieararchies.

While a class can only inherit from one parent class, it can implement several interfaces. In its pure sense, an interface does not have any implementation, but just declare abstract methods which must be implemented by all classes implementing it. And you still can define variables of the interface type, and then assign all types of classes implementing this interface tto the variable, and call the interface methods on the interface variable.

If you e. g. have an interface

interface HasName {
    void change_name(newName);
}

then both classes could implement that. A more meaningful example would e. g. be two classes Dog and Girl, both implementing the HasName interface, but not being the ancestor of each other neither having a common ancestor.

FrankPl
  • 919
  • 3
  • 12
  • To use an interface is to say that a `User` is-a `Book` or that a `Book` is-a `User`. Neither are appropriate. – Makoto Mar 09 '21 at 16:17
  • 1
    @Makoto No, it states that they have common behavior. It does not state common ancestry. That would be class hierarchy, not interfaces. – FrankPl Mar 09 '21 at 16:18
  • [The Liskov substitution principle](https://springframework.guru/principles-of-object-oriented-design/liskov-substitution-principle/) states otherwise. This is a fundamental tenet of how certain abstractions can be implemented in higher order applications, as well as how various dependency injection libraries behave. If something is related to something through an interface hierarchy, then it can always be regarded as *that* thing. Common examples are collections; an `ArrayList` and a `LinkedList` are always going to be `List`s. – Makoto Mar 09 '21 at 16:21
  • @Makoto I extended my answer. Why would you think that of two classes imoplementing an interface, 'is-a' muts be the relationship between them? It must only be the case that bfor both classes, 'is-a' is the relationship between each class ant the common interface they implement. So in the original example, both, `User` and `Book` are `NamedObject`s, but neither a book is a user nor a user is a book. Exaxtly that is the purpose of interfaces. – FrankPl Mar 09 '21 at 16:27
  • For your extended response, I refer you to the second half of my answer. Practically speaking, a book doesn't change its name on a whim; that's usually a new book. Users can change their name on a whim, though. – Makoto Mar 09 '21 at 16:55
  • @Makoto `SQLException` and `ArrayList` both implement the interface `iterable`, yet an `ArrayList` is obviously not an `SQLException` and vice versa. 2 Classes that implement an interface do not need to be in a is-a relationship to each other. They just both need a is-a relationship to the interface they implement. So both an `ArrayList` and an `SQLException` is-a `iterable` in the same way that both `Book` and `User` could be a `HasName`. Now of course whether using an interface for the name makes sense is another discussion, but it wouldn't be wrong – OH GOD SPIDERS Mar 09 '21 at 16:56
  • @OHGODSPIDERS: The utility in that is, because you can have chained exceptions in `SQLException`, you get to leverage the syntactic sugar of the for-each loop since it is an iterable. `for(Exception nested : sqlException)` as a for-instance. I wouldn't exactly agree with that myself, so I'd only give advice that is consistent and makes sense with what I write and how I support Java apps day to day. – Makoto Mar 09 '21 at 16:59
  • @Makoto You misunderstood me. I'm fully aware why it makes sense for both SQLException and ArrayList to implement iterable. I was simply using it as an example too point out that your first comment here ("To use an interface is to say that a User is-a Book or that a Book is-a User") is not correct. Two classes that implement an interface do not need to be in a is-a relaionship with each other. That is all i was trying to say. – OH GOD SPIDERS Mar 09 '21 at 17:10
  • @OHGODSPIDERS: That too is factually incorrect. You can do `Iterable exceptionIterable = new SQLException()` if you wanted to, or pass it into something that accepted an `Iterable`. – Makoto Mar 09 '21 at 17:17
  • @Makoto I don't know what this is referring to as it has nothing to do with what i just said. – OH GOD SPIDERS Mar 09 '21 at 17:24
  • It does @OHGODSPIDERS. You're stating that there isn't an is-a relationship between an implementation and an interface, but I've *demonstrated* that an `SQLException` is-an `Iterable`. Abusing interfaces because it's convenient to do so is a mistake without fully understanding the ramifications of doing so. – Makoto Mar 09 '21 at 17:28
  • @Makoto No. I stated that there isn't a is-a relationship between **two different classes implementing the same interface**! Which is what you wrongly stated in your first comment: "To use an interface is to say that a `User` is-a `Book` or that a `Book` is-a `User`" <- This is what you stated in your first comment here and it is wrong. `User implements someInterface` and `Book implements someInterface` doesn't imply or say "User is-a Book" despite them both implementing the same interface. – OH GOD SPIDERS Mar 09 '21 at 17:31
  • Oh, I see. I can see how that could be confusing. My mistake, @OHGODSPIDERS! If the interface was implemented then it could be an is-a relationship with `HasName`, which - again - I strongly question why a book would ever change its name. – Makoto Mar 09 '21 at 17:35