Interfaces and Abstract classes confusion in Java

2019-02-01 21:39发布

I'm having trouble understanding when to use an interface as opposed to an abstract class and vice versa. Also, I am confused when to extend an interface with another interface. Sorry about the long post, but this is very confusing.

Creating shapes seems like a popular starting point. Let's say we want a way to model 2D shapes. We know that each shape will have an area. What would be the difference between the following two implementations:

with interfaces:

public interface Shape {
    public double area();
}

public class Square implements Shape{
    private int length = 5;
    public Square(){...}

    public double area()
         return length * length;
    }
}

with abstract class:

abstract class Shape {
    abstract public double area();
}

public class Square extends Shape {
    private length = 5;
    public Square(){...}

    public double area(){
        return length * length;
    }

I understand that abstract classes allows you to define instance variables and allows you to give method implementations whereas an interface cannot do these things. But in this case, it seems like these two implementations are identical. So using any one is fine?

But now say we want to describe different types of triangles. We can have an isosceles, acute, and right angle triangles. To me, it makes sense to use class inheritance in this case. Using the 'IS-A' definition: a Right Triangle "IS-A" Triangle. A Triangle "IS-A" Shape. Also, an abstract class should define behaviors and attributes that are common within all subclasses, so this is perfect:

with abstract class

abstract Triangle extends Shape {
    private final int sides = 3;
}
class RightTriangle extends Triangle {
    private int base = 4;
    private int height = 5;

    public RightTriangle(){...}

    public double area() {
        return .5 * base * height
    }
}

We can do this with interfaces as well, with Triangle and Shape being interfaces. However, unlike class inheritance (using 'IS-A' relationship to define what should be a subclass), I'm not sure how to use an interface. I see two ways:

First way:

  public interface Triangle {
      public final int sides = 3;
  }
  public class RightTriangle implements Triangle, Shape {
      private int base = 4;
      private int height = 5;

      public RightTriangle(){}
      public double area(){
          return .5 * height * base;
      }
  }

Second way:

public interface Triangle extends Shape {
     public final int sides = 3;
} 
public class RightTriangle implements Triangle {
    ....

    public double area(){
         return .5 * height * base;
    }
}

It seems to me like both of these ways work. But when would you use one way over the other? And are there any advantages to using interfaces over abstract classes to represent different triangles? Even though we complicated the description of a shape, using interface vs abstract class still seem equivalent.

A critical component to interfaces is that it can define behaviors that can be shared across unrelated classes. So an interface Flyable would be present in classes Airplane as well as in Bird. So in this case, it is clear that an interface approach is preferred.

Also, to build off of the confusing interface extending another interface: When should the 'IS-A' relationship be ignored when deciding on what should be an interface? Take this example: LINK.

Why should 'VeryBadVampire' be a class and 'Vampire' be an interface? A 'VeryBadVampire' IS-A 'Vampire', so my understanding is that a 'Vampire' should be a superclass (maybe abstract class). A 'Vampire' class can implement 'Lethal' to keep its lethal behavior. Furthermore, a 'Vampire' IS-A 'Monster', so 'Monster' should be a class as well. A 'Vampire' class can also implement an interface called 'Dangerous' to keep its dangerous behavior. If we wish to create a new monster called 'BigRat' which is dangerous but not lethal, then we can create a 'BigRat' class which extends 'Monster' and implements 'Dangerous'.

Wouldn't the above achieve the same output as using 'Vampire' as an interface (described in the link)? The only difference I see is that using class inheritance and preserving the 'IS-A' relationship clears up a lot of confusion. Yet this is not followed. What is the advantage of doing this?

Even if you wanted a monster to share vampiric behavior, one can always redefine how the objects are represented. If we wanted a new type of vampire monster called 'VeryMildVampire' and we wanted to create a vampire-like monster called 'Chupacabra', we can do this:

'Vampire' class extends 'Monster' implements 'Dangerous', 'Lethal', 'BloodSuckable'
'VeryMildVampire' class extends 'Vampire' class
'Chupacabra' class extends 'Monster' implements 'BloodSuckable'

But we can also do this:

'VeryMildVampire' extends 'Monster' implements Dangerous, Lethal, Vampiric
'Chupacabra' extends 'Monster' implements Dangerous, Vampiric

The second way here creates a 'Vampiric' interface so that we can more easily define a related monster rather than create a bunch of interfaces which define vampiric behaviors (like in the first example). But this breaks the IS-A relationship. So I'm confused...

7条回答
Summer. ? 凉城
2楼-- · 2019-02-01 22:20

Your shape example is good. I look at it this way:

You only have abstract classes when you have methods or member variables that are shared. For your example for Shape you've only got a single, unimplemented method. In that case always use an interface.

Say you had an Animal class. Each Animal keeps track of how many limbs it has.

public abstract class Animal
{
    private int limbs;
    public Animal(int limbs)
    {
        this.limbs = limbs;
    }

    public int getLimbCount()
    {
        return this.limbs;
    }

    public abstract String makeNoise();
}

Because we need to keep track of how many limbs each animal has, it makes sense to have the member variable in the superclass. But each animal makes a different type of noise.

So we need to make it an abstract class as we have member variables and implemented methods as well as abstract methods.

For your second question, you need to ask yourself this.

Is a Triangle always going to be a shape?

If so, you need to have Triangle extend from the Shape interface.

So in conclusion - with your first set of code examples, choose the interface. With the last set, choose the second way.

查看更多
登录 后发表回答