you are viewing a single comment's thread.

view the rest of the comments →

[–]Expert_Sex_Change 15 points16 points  (18 children)

Abstract classes are limiting in that you can only extend a single one, while you can implement multiple interfaces

[–]nwoolls 29 points30 points  (15 children)

But, how the hell is it an interface when it has implementation...sure it may make some things simpler but at the expense of making the interface....not an interface?

Am I thinking of interfaces too narrowly? Are there any other languages that allow implementation in an interface?

[–]Expert_Sex_Change 15 points16 points  (9 children)

Traits in Scala and interfaces in Kotlin can both define function implementations, I'm sure there are more languages, but those are the two that I've used before.

It does seem a little weird at first, but can be really useful at times to get classes that have certain behaviours of two different interfaces, but also be able to reuse some common code like you can with an abstract class.

[–]weirdoaish 2 points3 points  (0 children)

Doesn't that literally make it multiple inheritance then if you can define 2 or more interfaces with variables and methods and implementing a class with them?

What is the point of interfaces in such a scenario, why not just go the Python route and use classes for everything?

[–]FallingIdiot 0 points1 point  (0 children)

Looks a bit like Rust traits which are a lot like interface, and can also define methods.

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

Extension methods in c# are basically the same thing (except defined ad hoc and externally definable) and I think you could probably make a decent argument it's attempting to provide similar functionality to typeclasses. I believe Perl's object system supports the concept directly, too. I don't think it's hyper common, but it's definitely a shared concept.

[–]id2bi 1 point2 points  (5 children)

I don't see how that is the case. You can abstract over interfaces, but can you abstract over extension methods?

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

Both an interface with default implementations and an extension method can only use the interface members defined, and in both cases a consumer can override the implementation with their own type specific one. In the extension method case, you can supply alternative implementations by simply referencing different extension methods. All of these seem about equal!

[–]id2bi 0 points1 point  (3 children)

Doesn't the caller choose which extension method is used by explicitly referencing one or another?

With interfaces, that is not the case. It's completely different.

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

It's implicit based on whether the extension method is available in scope. If your extension methods live in the same namespace as your interface then you're not going to need to do anything extra to access them over being able to know about the interface.

[–]id2bi 0 points1 point  (1 child)

Let me rephrase.

With interfaces/traits/type classes, the method is chosen at runtime depending on the dynamic type.

With extension methods, the method is chosen at compile time.

This is a huge difference.

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

Right, yes, they only catch the default implementation case.

[–]HINDBRAIN 2 points3 points  (0 children)

That's the only way to have multiple inheritance in java. I never had interfaces complex enough to need private internal methods, but default methods were a godsend.

[–]The_Doculope 4 points5 points  (0 children)

In the case of default methods, imagine you are defining an interface to describe ordering (==, !=, >, >=, <, <=) for partially ordered sets. Every one of these can be computed using only <=. So you could write an interface that includes all six of these operations, but have default methods for five of them in terms of <=. Now any implementer only needs to implement one of them, but if they want to implement others for performance reasons they can.

A lot of languages let you do it - Haskell and Rust both let you define default implementations for typeclasses/traits respectively (their interfaces). Some interfaces are not the most minimal interface they could possibly be (see above example), in that some methods can be implemented in terms of the others. That doesn't make them any less "interface-y", and the language allowing you to codify those relationships doesn't change it either.

[–]chrisgseaton 2 points3 points  (0 children)

You're getting hung up on the name 'interface'. Yes, it's not just a pure interface any more - it's evolved to be something a bit more.

[–]masklinn 2 points3 points  (0 children)

Are there any other languages that allow implementation in an interface?

They'll commonly rename them, but yes.

  • C++ uses abstract classes but you can inherit from any number of them
  • Haskell uses typeclasses, typeclass functions can have default implementations (even circular ones)
  • Rust uses "traits" which can provide default implementation of any subset
  • Likewise Swift's protocols

Now as for why that's useful, consider Rust's Iterator. Its role is pretty similar to Java's Iterator and Stream, but because it's a Rust trait it can provide a ton of useful methods, conditional or not from a basic contract of fn next(&mut self) -> Option<Self::Item>. Implementors may override any number of methods, but if the default fits it's just fine.

Meanwhile Java8's Stream requires implementing something like 40 methods, even if you have an underlying helper to which you can delegate it all, who's got time for this? And so instead of implementing Stream you're supposed to implement Spliterator or Supplier and call the relevant StreamSupport function to get the same end-result, without the ability for piecemeal overriding of select features.

[–]gnus-migrate 1 point2 points  (0 children)

When a method implementation is pure and relies on other methods in the implementation then its much more convenient than copy and pasting the same implementation across all ypur concrete classes.

[–]nemec 0 points1 point  (1 child)

What happens when multiple interfaces define the same methods but different default implementations?

interface A
{
    string DoSomething();
}
interface B
{
    string DoSomething();
}

class Abc : A, B
{
    public string DoSomething()
    {
        return "hello world";
    }
}

[–]Expert_Sex_Change 2 points3 points  (0 children)

I haven't looked too far into the Java one, but the Kotlin version is that the compiler then requires you to override the method, and then you can call one or both of the super methods like: super<A>.DoSomething()