第9章 多态
练习1:
修改第8章练习9中的Rodent,使其成为一个抽象类。只要有可能就将Rodent的方法声明为抽象方法。
package rodent;
import static testPackage.MyPrint.*;
public abstract class Rodent {
public Rodent(TotalNum totalNum){
totalNum.addTotal();
}
public abstract void predation();
}
练习2:
创建一个不包含任何抽象方法的抽象类,并验证我们不能为该类创建任何实例。
代码
package rodent;
public abstract class AbstractCompletely {
public static void main(String[] args) {
// new AbstractCompletely();
}
}
编译器报错
练习3:
创建一个基类,让他包含抽象方法print(),并在导出类中覆盖该方法。覆盖后的方法版本可以打印导出类中定义的某个整形变量的值。在第一该变量之处,赋予它非零值。在基类的构造器中调用这个方法。现在,在main()方法中,创建一个导出类对象,然后调用它的print()方法。解释发生的情形。
代码
package rodent;
public abstract class AbstractCompletely {
public AbstractCompletely(){
print();
}
abstract void print();
public static class PrintClass extends AbstractCompletely {
int a = 12;
public PrintClass() {
print();
}
@Override
void print() {
System.out.println(a);
}
}
public static void main(String[] args) {
PrintClass printClass = new PrintClass();
printClass.print();
}
}
输出
0
12
12
输出顺序和运行顺序有关,详见第八章,继承类的执行流程。导出类调用的为覆盖后的方法。
练习4:
创建一个不包含任何方法的基类,从它那里导出一个类,并添加一个方法。创建一个静态方法,它可以接受指向基类的引用,将其向下转型到导出类,然后再调用该静态方法。在main()中,展现它的运行情况。然后,为基类中的方法加上abstract声明,这样就不在需要进行向下转型。
代码
package rodent;
abstract class A {
}
class B extends A {
void show() {
System.out.println("B is calling");
}
}
abstract class C {
abstract void show();
}
class D extends C {
@Override
void show() {
System.out.println("C is calling");
}
}
public class Main {
static void getShow1(A a) {
((B) a).show();//必须需要向下转型
}
static void getShow2(C c) {
c.show();//不需要向下转型
}
public static void main(String[] args) {
A a = new B();
getShow1(a);
C c = new D();
getShow2(c);
}
}
输出
B is calling
C is calling
练习5:
在某个包内创建一个接口,内含三个方法,然后在另一个包中实现此接口。
同练习6;
练习6:
证明接口内所有的方法自动是public
代码
package testInterface;
public interface Instrument {
void toning();
void play();
void maintain();
}
package testPackage;
import testInterface.Instrument;
public class Piano implements Instrument {
@Override
public void toning() {
}
@Override
public void play() {
}
@Override
public void maintain() {
}
}
其他包仍可访问,说明是public。
练习7:
修改第8章中的练习9,使Rodent成为一个接口。
略,修改为接口,然后去掉Rodent构造器,将extend关键字修改为implement即可。
练习8:
在polymorphism.Sandwich.java中,创建接口FastFood并添加合适的方法,然后修改Sandwich以实现FastFood接口。
略。
练习9:
重构Music5.java,将在Wind、Pecussion和Stringed中的公共方法移入一个抽象类中。
略。
练习10:
略。
练习11:
创建一个类,它有一个方法用于接受一个Sting类型的参数,生成的结果是将该参数的每一对字符串进行互换。对该类进行适配,使得它可以用于interfaeprocessor.Apply.process。
代码
package processor;
public interface Processor {
String name();
Object process(Object input);
}
package processor;
public class SwapStringAdapter implements Processor {
@Override
public String name() {
return toString();
}
@Override
public Object process(Object input) {
String s = "";
try {
char temp;
char[] chars = ((String) input).toCharArray();
for (int i = 0; i < chars.length; i += 2) {
temp = chars[i];
chars[i] = chars[i + 1];
chars[i + 1] = temp;
}
s = String.copyValueOf(chars);
} catch (Exception e) {
e.printStackTrace();
}
return s;
}
}
package processor;
public class Apply {
public static void process(Processor processor,Object s){
System.out.println("using processor: " + processor.name());
System.out.println(processor.process(s));
}
}
package processor;
public class Main {
public static void main(String[] args) {
Apply.process(new SwapStringAdapter(),"new String");
}
}
输出
using processor: processor.SwapStringAdapter@4554617c
en wtSirgn
此题看起有些复杂,实际上只需要将Apply方法理解为一个黑盒,接受Processor工具对象,和待处理对象,产生对应的输出,还是很简单的。
练习12:
在Adventure.java中,按照其他接口的样式,增加一个CanClimb接口。
代码
package adventure;
public interface CanClimb {
void climb();
}
package adventure;
public interface CanFight {
void fight();
}
package adventure;
public interface CanFly {
void fly();
}
package adventure;
public interface CanSwim {
void swim();
}
package adventure;
public class ActionCharacter {
//和CanFight相同特征签名的方法
public void fight() {
System.out.println("ActionCharacter fight");
}
}
package adventure;
import static testPackage.MyPrint.*;
public class Hero extends ActionCharacter implements CanClimb, CanFight, CanFly, CanSwim {
@Override
public void climb() {
println("Hero climb");
}
@Override
public void fly() {
println("Hero climb");
}
@Override
public void swim() {
println("Hero climb");
}
}
package adventure;
public class Adventure {
public static void t(CanFight canFight) {
canFight.fight();
}
public static void u(CanClimb canClimb) {
canClimb.climb();
}
public static void v(CanFly canFly) {
canFly.fly();
}
public static void w(CanSwim canSwim) {
canSwim.swim();
}
public static void x(ActionCharacter character) {
character.fight();
}
public static void main(String[] args) {
Hero hero = new Hero();
t(hero);
u(hero);
v(hero);
w(hero);
x(hero);
}
}
输出
ActionCharacter fight
Hero climb
Hero climb
Hero climb
ActionCharacter fight
练习13:
创建一个接口,并从该接口继承两个接口,然后从后面两个接口多重继承第三个接口。(c++菱形问题,java对于相同名称的方法认为是同一个方法)。
代码
package multipleInheritance;
public interface First {
void output();
}
package multipleInheritance;
public interface SecondA extends First {
@Override
void output();
}
package multipleInheritance;
public interface SecondB extends First{
@Override
void output();
}
package multipleInheritance;
public interface Third extends SecondA,SecondB{
@Override
void output();
}
package multipleInheritance;
public interface Fourth extends Third{
@Override
void output();
}
package multipleInheritance;
public class Main implements Fourth {
@Override
public void output() {
System.out.println("Main output");
}
public static void main(String[] args) {
Main main = new Main();
main.output();
}
}
输出
Main output
练习14:
创建三个接口,每个接口都包含两个方法。继承出一个接口,它组合了这三个接口并添加了一个新方法。创建一个实现了该新接口并且继承了某个具体类的类现在编写四个方法,每一个都接受这四个接口之一作为参数。在main()方法中,创建这个类的对象,并将其传递给这四个方法。
略,此题类似于将练习12和练习13组合起来。
练习15:
将前一个练习修改为:创建一个抽象类,并将其继承到一个导出类中。
略。
练习16:
创建一个类,它将生成一个char序列,适配这个类,使其可以成为Scanner对象的一种输入。
代码
package scannerAdapter;
import java.util.Random;
public class RandomChar {
private static final Random random = new Random(47);
public char next() {
return (char) random.nextInt();
}
}
package scannerAdapter;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Scanner;
public class RandomCharAdapter extends RandomChar implements Readable {
private int count;
public RandomCharAdapter(int count) {
this.count = count;
}
@Override
public int read(CharBuffer cb) throws IOException {
if (count-- == 0) {
return -1;
}
String a = next() + " ";
cb.append(a);
return cb.length();
}
public static void main(String[] args) {
Scanner scanner = new Scanner(new RandomCharAdapter(10));
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
}
}
输出
䊕
Ԗ
ફ
汊
䰃
ꛛ
⍨
榱
ࢴ
应该是对应int值的原因,输出一堆火星文,Random类不熟悉。
测试发现,输出int都是这样的:
-1172028779
1717241110
-2014573909
229403722
688081923
-1812486437
809509736
1791060401
-2076178252
-1128074882
有时间还要研究一下。
练习17:
证明在接口中的域隐式地是static和final的。
略。这个很显然。
练习18:
创建一个Cycle接口及其Unicycle、Bicycle和Tricycle实现。对每种类型的Cycle都创建相应的工厂,然后编写代码使用这些工厂。
代码
package testPackage;
import static testPackage.MyPrint.*;
public interface Cycle {
public void produce();
public static class Unicycle implements Cycle {
@Override
public void produce() {
println("Unicycle produce");
}
}
public static class Bicycle implements Cycle {
@Override
public void produce() {
println("Bicycle produce");
}
}
public static class Tricycle implements Cycle {
@Override
public void produce() {
println("Tricycle produce");
}
}
interface ProduceFactory {
Cycle produceCycle();
}
class UnicycleFactory implements ProduceFactory {
@Override
public Cycle produceCycle() {
println("UnicycleFactory");
return new Unicycle();
}
}
class BicycleFactory implements ProduceFactory {
@Override
public Cycle produceCycle() {
println("BicycleFactory");
return new Unicycle();
}
}
class TricycleFactory implements ProduceFactory {
@Override
public Cycle produceCycle() {
println("TricycleFactory");
return new Tricycle();
}
}
class Cycles {
static void produceCycle(ProduceFactory produceFactory) {
produceFactory.produceCycle().produce();
}
public static void main(String[] args) {
produceCycle(new UnicycleFactory());
produceCycle(new BicycleFactory());
produceCycle(new TricycleFactory());
}
}
}
输出
UnicycleFactory
Unicycle produce
BicycleFactory
Unicycle produce
TricycleFactory
Tricycle produce
练习19:
使用工厂方法编写一个框架,它可以执行抛硬币和掷骰子功能。
代码
package testPackage;
import static testPackage.MyPrint.*;
public interface Game {
void play();
class Coins implements Game {
@Override
public void play() {
println("tossing");
}
}
class Throw implements Game {
@Override
public void play() {
println("throwing");
}
}
interface GameFactory {
Game getGame();
}
class CoinsFactory implements GameFactory {
@Override
public Game getGame() {
return new Coins();
}
}
class ThrowFactory implements GameFactory {
@Override
public Game getGame() {
return new Throw();
}
}
class Player {
static void playGames(GameFactory factory) {
factory.getGame().play();
}
public static void main(String[] args) {
playGames(new CoinsFactory());
playGames(new ThrowFactory());
}
}
}
输出
tossing
throwing
第九章备注
优先使用类而不是接口。如果设计中需要某个接口,则必须了解它。否则不到迫不得已,不要将其放到设计中。