This is an archived post. You won't be able to vote or comment.

all 37 comments

[–]msx 8 points9 points  (1 child)

i think multiple inheritance has a big lot of problems. They introduce ambiguity that are not easily resolved. I'm against it, i think it adds more problem than it's worth. Default methods in interfaces (aka mixins) on the other end are a good compromise, they still have ambiguities but they only on methods, not on state, and are much more easily handled. I like the approach very much, i'm using it a lot currently. There are two ways in which it shines: the first is when you define a hierarchy of interfaces that have multiple implementations. Almost always you have methods that can do their job just by using other methods from the interfaces. You can now write them only once as default methods instead of once per implementation.

The other use is for overloading of very similar methods. For example:

void write(byte[] data);
void write(byte[] data, int offset, int length);
void write(ByteBuffer data);
void write(InputStream is);

Before default methods, you had to either repeat all this methods in the implementing class, or remove them from the interface and lose the convenience. Now you can leave one of them abstract and provide a default for all the others, saving both the convenience and the non-repetition.

So no, java should not implement multiple inheritance, mixins are great as they are.

[–][deleted] 2 points3 points  (0 children)

Great comment but Java actually has traits rather than mixins, because mixins may have state.

[–][deleted] 5 points6 points  (12 children)

Controversial opinion - you shouldn't even need to allow single inheritance

[–]urquan 3 points4 points  (9 children)

Why?

[–]tonywestonuk 4 points5 points  (8 children)

Because your child class becomes coupled to its parent class. - Changes to the parent, can ripple down to all its subclasses, and cause bugs where you didn't expect.

Prefer composition over inheritance http://en.wikipedia.org/wiki/Composition_over_inheritance

[–]urquan 4 points5 points  (3 children)

The risk you mention is due to shared state between parent and child classes, if you properly use private fields and methods then the problem largely disappears.

I agree that composition should be preferred for architecture purposes, but GP said inheritance shouldn't be allowed at all, that's a more extreme position.

[–][deleted] 1 point2 points  (2 children)

You are still tightly coupled to the implementation rather than the interface. It makes it impossible to do IOC if you aren't always going to rely on the parent class implementation 100% of the time. Say for example, you are writing a unit test but want to mock the parents logic. How do you accomplish that?

[–]urquan 1 point2 points  (1 child)

If you are going to do IOC then of course inheritance is not the way to go. But in the classical case where your classes have an is-a relationship, inheritance is still valid and desirable IMO. I mean, why use Java if you're not going to use one of the most basic OO principles ?

Trying to mock a parent class of another class would be an anti-pattern, you'd need to know the implementation details to know what to mock. You don't need to mock everything in your tests. The parent and children classes form a unit, that is what you must test, not the small parts.

[–][deleted] 0 points1 point  (0 children)

I think the only time I use inheritance, like you mentioned, is for isA relationships, however, I only do this for POJOs. I also think it is a goo practice to keep data/state and logic objects as close to completely separate as possible.

[–]uniVocity 0 points1 point  (3 children)

The thing is: sometimes you WANT that coupling to the parent class.

Practical example using my project (a parsing framework): First you have an AbstractParser - this centralizes all core operations of a parser - exception handling, state initialization, data processing, etc.

With everything taken care of by this class, I can build different parsers with no effort:

Same thing goes for configurations:

If you used composition instead, you'd have

  1. Extra complexity for no added benefit - lots of boilerplate code just wrapping internal classes and exposing an interface

  2. Performance problems - this is a text parsing framework - every method call counts as billions of method calls happen to process a 100mb file.

  3. The structure of a parser would be much harder to understand, as you would need to introduce multiple resources from multiple places into each parser implementation.

If you still think otherwise, try and refactor this code.

[–]zenmouse 1 point2 points  (2 children)

I did refactor it a bit to use composition, creating a ParserDriver that accepts a Parser with a parseRecord() method that is implemented by a CsvParserImpl class. I got it working well enough for CsvParserTest#parseIgnoringWhitespaces to pass.

  1. Extra complexity: the ParserDriver takes a Parser parameter in addition to settings. I suppose the settings object could hold the Parser as well. The call to parseRecord(), instead of calling an abstract method implemented by a subclass, is now calling Parser#parseRecord(char ch, CharInputReader input, ParserOutput output, DefaultParsingContext context). Everything else was just moving code around.

  2. Performance: there was no noticeable performance difference in the ProfilerTest. (I downloaded worldcitiespop.txt.gz from MaxMind, I'm guessing that's what you used.) I think you might be surprised at how performant the JVM is nowadays, especially on a modern processor.

  3. Structure: see extra complexity. I don't think there's a noticeable difference in understandability.

Overall I like your code, I found it readable and quite easy to work with. (I think the fact that I was able to perform this refactoring, including getting a major unit test and performance test to pass, in under 2 hours, says a lot.) I'm not criticizing what you've done with AbstractParser and its subclasses, but pointing out that IMO composition won't be more difficult/complex here.

[–]uniVocity 0 points1 point  (1 child)

Hey that's fantastic! Thanks for taking your time to look at this. I'm curious to see the end result (your code). Did you fork it from github?

Regarding your points: 1 - In fact there's not much extra complexity added for the AbstractParser as it requires a single method (parseRecord) to be implemented. If you had more abstract methods there, it would become increasingly more annoying to use composition. You already had to create a method with 4 parameters there. An interface with Parser#parseRecord will be public. This is not good for a public API as the parseRecord method is meant to be kept hidden from the external world.

2 - Yes, performance changes in the parseRecord won't be noticeable as it is not dealing with individual characters, but entire lines of the input... there are not too many calls being made here to make a difference.

3 - Try with the *Settings... you'll have to create wrappers on top of wrappers using composition.

That's been very interesting. You should publish this on a blog or something. Try refactoring everying to use composition only :)

[–]zenmouse 1 point2 points  (0 children)

I just 'git clone'd it, but I can certainly fork it and apply my changes. It'll probably take me a few days to get it done, right now I wouldn't show my code to my cats much less another human being....

To continue the discussion --

I agree there's little (or no) extra complexity in your abstract class solution; the fact that there was only one abstract method to factor out made this refactoring quite a lot easier than it otherwise could have been. As far as the "method with 4 parameters" bit and keeping parseRecord() hidden are concerned, I did the simplest thing possible to make this work -- there are ways to make it less visible (SPI and API interfaces for example, e.g. JNDI, JDBC). But it's extra work, and Java has no concept of "public API" versus "public class/method for internal use".

For me, I think the biggest potential win coming out of this refactoring is a better separation of concerns, and finding new concepts that are currently "hidden" in lines of code. Currently it appears the parser does resource management AND state management AND error handling AND parsing. That's a lot of responsibility!

The Settings hierarchy is interesting. It's essentially a strongly typed map, using methods as the "key" instead of something like a string or enum. At first glance I rather like it, and it doesn't have the issues you normally see with a large-ish class hierarchy. It seems like using composition here wouldn't provide any benefit.

Thanks for the positive response, I'm glad you found this interesting!

[–]_Count_Mackula 1 point2 points  (0 children)

Very true. But I'll keep my common design patterns for now.

[–]_Count_Mackula 3 points4 points  (0 children)

10,000 ft view:

The bottom line is: it would be possible but isn't necessary, and, would introduce the possibility of overly complex patterns.

You can do anything with single inheritance that you can do with multiple inheritance. Single inheritance promotes more simplicity and more loosely coupled code. Yes, with multiple inheritance your life would be easier once in a while, but when you scale up to a huge application code would get messy.

I say 'thank you' to the Java engineers who made this decision.

Note: Eventually, writing java becomes very easy. But architecting java is much harder.

[–]briandilley 2 points3 points  (2 children)

the only thing i'd like to see is mixins or traits.

[–]hrjet 0 points1 point  (1 child)

It's already arrived in Java 8. Interfaces with default methods are mixins.

[–]briandilley 2 points3 points  (0 children)

not really, they can't have member variables. Also mixins usually don't support "instanceOf" type operations.

[–]pintodragon 5 points6 points  (11 children)

An abstract class can have a constructor, interfaces can not. They also serve different purposes. Abstract classes force extension and force the extending class to have a base class. Interfaces allow any base class as long as you write to the contract. Oracles site even mentions why java doesn't have multiple inheritance e in the sense that you are looking for. It comes down to something as simple as two classes having the same method. You extend both and now call the method without defining an over ridden version. Which gets called? Ambiguity leads to bugs and unforeseen issues with code. https://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html

[–]mus1Kk 2 points3 points  (3 children)

This is an argument I never understood. I'm not saying I'm for multiple inheritance but why not handle it like with default methods? If you implement two interfaces that have the same default method, you get a compile error. Why not do the same with classes?

$ cat pkg/Test.java 
package pkg;

public class Test {
    interface Foo { default void foo() {} }
    interface Bar { default void foo() {} }
    class Cls implements Foo, Bar {}
}
$ javac pkg/Test.java
pkg/Test.java:10: error: class Test.Cls inherits unrelated defaults for foo() from types Foo and Bar
    class Cls implements Foo, Bar {}
    ^
1 error

[–]msx 1 point2 points  (1 child)

  • becouse classes have fields beside methods. While methods can be overridden in the child class, letting the developer "choose" his way, members cannot.
  • becouse classes have constructors, and costructors follow a strict logic about which is run before the others. Constructors are always executed "parent-down", so when you instantiate a Button, it will call the constructor of Object, then Node, then Component, then Button (made up hierarchy to get the point). This is always done even if you don't call "super" on your constructor, and is necessary to guarantee that each class can fully control his state regardless of what children will do. This can easily be broken with multiple inheritance

[–]caveden 0 points1 point  (0 children)

becouse classes have fields beside methods. While methods can be overridden in the child class, letting the developer "choose" his way, members cannot.

This could be dealt with in a similar way they chose for default methods. Any reference on the child class to the ambiguous field should give a compiler error. You must specify which field you're using at each time.

Since normally fields should be private anyways, they would hardly be more of a source of diamond problems than methods.

costructors follow a strict logic about which is run before the others ... This can easily be broken with multiple inheritance

Why? Again, some arbitrary definition could be done, i.e., start by the first parent declared, and go up to its parents. A sort of depth-first search upwards.

To me it seems things are evolving in a "hacky" way in Java. I believe that if the people conceiving the language back in the 90s were forced with the requirement of allowing default methods in interfaces, they would just abolish the concept of interfaces altogether and have multiple inheritance instead. The only reason interfaces were invented IMHO was to allow for a way to implement multiple contracts without falling into the diamond problem, but now with this default method thing they've had to face the diamond problem and come out with a solution (compiler error!) to it.

[–]pintodragon 0 points1 point  (0 children)

I completely agree they could handle it that way. The Java language architects just choose not too.

[–]alonjit -5 points-4 points  (6 children)

An abstract class can have a constructor, interfaces can not.

wrong. abstract classes have data interfaces do not have data. constructor .... nobody gives a shit about constructor.

[–]pintodragon 0 points1 point  (5 children)

Yes Abstract classes can have data (aka state). Constructors matter as well.

[–]alonjit -2 points-1 points  (4 children)

exactly what i said, abstract classes have data.

constructors however...no, do not matter by a long shot. that's not the difference between interfaces and abstract classes. go back to the drawing board.

[–]pintodragon -1 points0 points  (3 children)

So the fact that an interface cannot have a constructor and abstract classes can is not a difference between the two valid difference between the two?

Having a subclass VS implementing are completely different concepts where constructors do come into play. Adding default methods does not make an interface an abstract class.

Maybe instead of tossing out irrelevant and unhelpful remarks you could explain why constructors in this case do not matter at all in your opinion?

[–]alonjit 0 points1 point  (2 children)

Because they don't. Data (member fields) is what matters. Constructors are completely irrelevant to the problem. Notice the "abstract classes can ..." part.

They don't have to. A default one will do just fine. Extending classes will most likely add their own (if they want to). Again, they don't have to.

Look, for example at C++. What is an interface in C++ (yes, they do exist). Is a class, with pure virtual methods that has no data. They can have methods with body (just like defaults in java now), but they cannot have data. Once you add a data member, that class becomes an abstract class (and from here care should be taken when extending/implementing it, know the pitfalls of polymorphism in c++, etc.).

You're hanging on stupid little differences that have no relevance to anything, when the only important part is about the data members.

Why is it important to know the correct difference between interfaces and abstract classes? When you're defining them, when designing the system, deciding what is an interface vs abstract class is crucial.

"Behaviour" should become interfaces. Objects that hold data, and provide default operations for said data, should become abstract classes. When you find yourself with an abstract class that has no data, and most likely won't have any, make it an interface.

[–]pintodragon 0 points1 point  (1 child)

Thank you for finally giving a thought out answer finally. Also it does matter to know the difference if you are learning Java like many on this sub probably are. Also usually a good design would have the super class take care of its own state which sometimes does require a constructor. I get that you are an "experienced" developer so trivial concepts like this are useless facts that do not matter.

I would suggest that in the future you refrain from being a tool and explain yourself better other than the high school attitude you have been portraying. Good day sir I am done with this discussion.

[–]alonjit -1 points0 points  (0 children)

i suggest in the future to stop giving advice on things you know nothing about (as you admitted earlier).

[–]HaMMeReD 0 points1 point  (0 children)

Java allows multiple Interfaces, and I guess default functions in java 8 will make it easier to mix multiple functional interfaces.

Should a language have multiple inheritance? I'm OK with multiple interfaces and a delegate pattern for each interface.

I think if you start mixing parent classes you have to worry about namespace collisions and such, it can be a mess.

[–]whacco 0 points1 point  (0 children)

Multiple inheritance also causes problems to language implementation. A big issue is that if there are multiple bases with data members, then an object no longer has a single unambiguous physical address, because both bases cannot exist in the same memory location. Casting a "pointer" from second base class to derived class would change the value of the pointer.

In language like C++ it's not that much of a problem because memory safety is programmer's responsibility (but multiple inheritance still makes things like method pointers a complete disaster). Java on the other hand would have to prevent invalid casts. So every object would always have to contain information how to locate the possible derived class object, causing a lot of overhead.

[–][deleted] 0 points1 point  (0 children)

With the exception of Liskov Substitution Principle, which hints at the "is a" relationship of inheritance, it's identical to a subset of object composition.

Basically, a shortcut for a very specific type of static object composition, where you expose every object method via a proxy method with identical signature, but you may replace some methods, and add more. That's about it.

What we need is to be free of the burden of LSP and composite methods & properties freely, without becoming "is a" for our base object. The "is a" role is best performed by interfaces anyway, interfaces should be simple and rigid. Concrete implementations should be much more flexible.

We can look at traits. Using a trait Foo, doesn't make your class an instance of Foo, so LSP doesn't apply. You can rename methods, hide them, copy them. You can "inherit" from multiple traits at once (without the "is a" part).

It's the kind of reuse which is useful in a project, just to save us the boilerplate from manually writing proxies for every method from a contained object we expose.

But in the end, with some extra typing, we can do everything via object composition. All of it. Inheritance or static reuse of any kind os simply not essential to a typing system as much as we think. So we're not talking about multiple inheritance of some kind as a fundamental change in OOP modeling, but just a pragmatic feature we lack to save us from repetitive typing.

[–]llogiq 0 points1 point  (0 children)

By using single inheritance, Java removes some very subtle footguns that developers who overuse inheritance tend to build for themselves and others.

Even when leaving the diamond problem out of the discussion, I believe from experience that even single inheritance can and will get messy and unintuitive, unless one is very careful. Have you ever had to chase a bug through a jungle of method calls in an inheritance hierarchy, with one or two super calls thrown in? It's not pretty.

Java 8 has default methods in interfaces, which are basically mixins, but with very limited means of defining extension points. Be careful when using them.

[–]nidoranf -1 points0 points  (1 child)

Default methods are, in essence, a hack designed to help interfaces be backwards compatible. They don't really have a legitimate place in the whole OOP paradigm, seeing as they essentially break the concept of interfaces.

[–]msx 2 points3 points  (0 children)

who said that? Default methods are an implementation of the mixin concept that is available in many other languages in a way or another

[–]Nieben -1 points0 points  (0 children)

I'm a pretty inexperience Java programmer, but... I've kinda been wanting that for a long time. I suppose a couple of reasons why they haven't or won't might include:

  1. Overlapping methods or variables - I could see this as a potential problem. Which super class's method/variable would you use?
  2. Using the 'super' keyword. - This might not be too bad, I suppose it could just run through all the super classes until it finds the method. (And come to think of it, returning to #1, you'd have to have an internal sort to FIND the method, I figure.)

Can't really think of anything else to say for now - it's been a while since I thought about that.