cast to an abstract class…how this is possible?

2019-06-24 08:23发布

问题:

i am actually reading a book about design patterns in java and i am a newbie :)

http://www.amazon.com/Design-Patterns-Java-TM-Software/dp/0321333020/ at the chapter about composite pattern i came across a code that puzzles me, a cast to an abstract class, I have also not well understood what happens when the sub-clase calls the constructor of the abstract superclass, can you help me please !!

the cast that i am talking about is in the isTree( Set visited)

        MachineComponent c = (MachineComponent) i.next();
        if (visited.contains(c) || !c.isTree(visited)) 

How can we call the isTree method of the subclass after a cast to his abstract superclass while the isTree superclass method is abstract?

Here are snippets of the two classes:

package com.oozinoz.machine;
/*
 * Copyright (c) 2001, 2005. Steven J. Metsker.
 */

import java.util.*;
import com.oozinoz.iterator.ComponentIterator;

/**
 * Objects of this class represent either individual machines or composites of
 * machines.
 */

public abstract class MachineComponent {

    /*
     * Subclasses implement this to support the isTree() algorithm.
     */

    protected abstract boolean isTree(Set s);

    // rest of class omitted
}

2:

package com.oozinoz.machine;    
/*
 * Copyright (c) 2001, 2005. Steven J. Metsker.
 */

import java.util.*;
import com.oozinoz.iterator.ComponentIterator;
import com.oozinoz.iterator.CompositeIterator;

 /**
 * Represent a collection of machines: a manufacturing line, a bay, or a
 * factory.
 */

public class MachineComposite extends MachineComponent {
    protected List components = new ArrayList();

    /**
     * @param visited a set of visited nodes
     * @return true if this composite is a tree
     * @see MachineComponent#isTree()
     */

    protected boolean isTree(Set visited) {
        visited.add(this);
        Iterator i = components.iterator();
        while (i.hasNext()) {
            MachineComponent c = (MachineComponent) i.next();
            if (visited.contains(c) || !c.isTree(visited)) 
                return false;
        }
        return true;
    }

    // rest of class omitted
}

回答1:

This is the distinction between runtime type (actual type) and compile-time type.

The typecast to an abstract class MachineComponent is fine, because the actual object instance will actually be some non-abstract subclass of MachineComponent that implements all of the abstract methods.

The abstract MachineComponent class is the compile-time type of the assigned variable. But no actual instance is (or can be) created with that abstract class.



回答2:

The isTree is only abstract for the abstract class. Once the class has been initialized as a non-abstract, casting it to an abstract will not change its memory. Therefore, calling the method on an abstract class effectively calls it on the concrete implementation.

The reason why it's useful is for passing subclasses around via the abstract implementation.

Let's say a String class has 'length' method. There's no need to have a function for every possible String-type subclass. Instead, you cast to the basic (abstract) String that has the 'length' implementation, and pass that around.



回答3:

OK - let's try again:

Q: Can you cast to an abstract class?

A: Sure - of course you can

For example:

public class UseAbstract
{
  public static void main (String[] args)
  {
    // Instantiate an abstract class
    AbstractPet myDog = new Dog ();
    myDog.sound ();

    // Instantiate a concrete class
    Cat myCat = new Cat ();
    myCat.sound ();

    // Cast a concrete class to an abstract class
    AbstractPet somePet = (AbstractPet)myCat;
    somePet.sound ();
  }
}

abstract class AbstractPet
{
  void sound ()
  {
    System.out.println ("eek");
  }
}

class Dog extends AbstractPet
{
  void sound ()
  {
    System.out.println ("Woof");
  }
}

class Cat extends AbstractPet
{
  void sound ()
  {
    System.out.println ("meow");
  }
}


回答4:

Well, let's take Cat which is a subclass of the abstract class Animal.

You can cast Cat to Animal because a cat is an animal. It does everything a normal animal does, since it has the functions of an animal.

Subclassing an abstract class is the same. You can simply use "is a" because it's a subclass.

You can define a list of animals as a zoo, but it can have dogs and cats - after all, they're both animals.

Casting a Cat to an Animal doesn't actually make it less of a Cat than it was - you simply tell your code to treat it like it would treat any other animal.

Edit
In case you don't like cats (I don't either), let me redirect you to the Wikipedia article about inheritance. Abstract classes would be useless if you can't cast them.



回答5:

Q: Can you cast to an abstract class?

A: Sure - of course you can.

What you can't do is call an abstract method of an abstract class. (inserting from comment section -Andrew Barber- below) -- You are actually calling the concrete implementation of it in the subclass.