Java 如何在不使用实例或getClass()的情况下检查对象的类型

Java 如何在不使用实例或getClass()的情况下检查对象的类型,java,oop,design-patterns,Java,Oop,Design Patterns,示例情况: 我有一个电视抽象超类。它继承了两个子类。这两个子类都有工厂方法来创建自己的远程。Remote是一个超类,它有两个子类。遥控器可以改变各自电视的频道(在这种情况下,三星遥控器应可与任何三星电视配合使用) 远程类有一个changeChannel方法,用于接收电视和频道。我的问题是,有没有一种方法可以让这种层次结构与它目前拥有的方法和参数保持一致,而不必使用条件逻辑,使遥控器只能改变其自有品牌电视的频道。我提供了下面的代码 import java.util.*; public abstr

示例情况: 我有一个电视抽象超类。它继承了两个子类。这两个子类都有工厂方法来创建自己的远程。Remote是一个超类,它有两个子类。遥控器可以改变各自电视的频道(在这种情况下,三星遥控器应可与任何三星电视配合使用)

远程类有一个changeChannel方法,用于接收电视和频道。我的问题是,有没有一种方法可以让这种层次结构与它目前拥有的方法和参数保持一致,而不必使用条件逻辑,使遥控器只能改变其自有品牌电视的频道。我提供了下面的代码

import java.util.*;

public abstract class Television{
    private int channel;

    public abstract Remote makeRemote();

    public int getChannel(){
        return channel;
    }

    public void setChannel(int c){
        channel=c;  
    }
}

import java.util.*;


public class SamsungTelevision extends Television{
    private int channel;

    public Remote makeRemote(){
        return new SamsungRemote();
    }   

}

import java.util.*;


public class SonyTelevision extends Television{
    private int channel;

    public Remote makeRemote(){
        return new SonyRemote();
    }

}

import java.util.*;


public abstract class Remote{

    public abstract void changeChannel(Television t,int channel);
}

import java.util.*;


public class SamsungRemote extends Remote{

    public void changeChannel(Television t,int channel){
        t.setChannel(channel);
        System.out.println("Samsung: Channel has been switched");
    }

}

import java.util.*;


public class SonyRemote extends Remote{

    public void changeChannel(Television t,int channel){
        t.setChannel(channel);
        System.out.println("Sony: Channel has been switched");
    }
}

import java.util.*;


public class Driver{
    public static void main(String[] args){
        Television t = new SamsungTelevision();
        Television t1 = new SonyTelevision();
        Remote r=t.makeRemote();
        r.changeChannel(t,35);
        System.out.println("Samsung current channel: " + t.getChannel());
        r.changeChannel(t1,37);
        System.out.println("Sony current channel: " + t1.getChannel());
    }
}

确保公共API中共存的类型层次结构可以共享代码的一种方法是使用和:

/**
*不同的制造商可以扩展此接口
*确保其产品在编译时的兼容性,同时
*使用使用泛型实现类型安全的标准API。
*/
公共接口制造商{
}
/**
*这个界面标志着三星创造的产品。
*/
公共接口扩展制造商{
}
/**
*这个界面标志着索尼创造的产品。
*/
索尼扩展制造商的公共接口{
}
公共抽象类电视{
专用int通道;
//这就确保了电视只制作遥控器
//由同一制造商生产
公共抽象远程makeRemote();
public int getChannel(){
返回通道;
}
公共频道(INTC){
通道=c;
}
}
公营三星电视台扩建电视台{
专用int通道;
公共远程makeRemote(){
返回新的SamsungRemote();
}   
}
公共类SonyTelevision扩展了电视{
专用int通道;
公共远程makeRemote(){
返回新的SonyRemote();
}
}
公共抽象类远程{
//这确保了远程设备只能与远程设备一起工作
//由同一制造商生产
公共频道(电视t、国际频道);
}
公共类SamsungRemote扩展了远程{
公共频道(电视t、国际频道){
t、 设置通道(通道);
System.out.println(“三星:频道已切换”);
}
}
公共类SonyRemote扩展了远程{
公共频道(电视t、国际频道){
t、 设置通道(通道);
System.out.println(“索尼:频道已切换”);
}
}
公务舱司机{
公共静态void main(字符串[]args){
电视t=新三星电视();
电视t1=新SonyTelevision();
远程r=t.makeRemote();
r、 变换通道(t,35);
System.out.println(“三星当前频道:+t.getChannel());
//生成编译时错误,因为r是三星遥控器,并且
//t1是一台索尼电视
//r、 变换通道(t1,37);
//System.out.println(“索尼当前频道:+t1.getChannel());
}
}
这很好,因为它创建了编译时安全性。人们甚至不能意外地编写一个违反制造商兼容性约束的程序

如果你想做一些可以与任何品牌的电视配合使用的事情,你也可以这样做:

public static <M extends Manufacturer> void doSomethingWithTVOfAnyMake(Television<M> tv){
        int myChannel = tv.getChannel();
        //do more stuff...
    }
使用TvofAnmake(电视)的公共静态无效DoSomething{
int myChannel=tv.getChannel();
//做更多的事情。。。
}

工厂方法可以为您实现这一点。根据GoF参考:

将其应用于TV创建遥控器的位置:

您不能在
changeChannel()
方法上使用
television
参数。这就是为什么你需要做检查的原因

这正是
Collection.iterator()
的工作原理。此外,许多具体的
迭代器
没有在JavaAPI中记录(它们是具体集合的内部类,请参阅)


您可以在设计中采用类似的方法,并说远程设备的客户端不需要知道它们是什么类,因为它们只想使用它们来更改通道。您可以使
SamsungRemote
成为
SamsungTV
的私有内部类,并且
createRemote()
方法始终返回一个向上转换的远程类型

而不必使用条件逻辑,遥控器就只能改变自己品牌电视的频道?不,有几种方法可以解决这个问题。如果你通过一台不同牌子的电视机,你是否在寻找会导致编译时错误的东西?允许您在多大程度上干扰现有的类?当前代码很好。你为什么要这张支票?。如果您只想知道执行代码的类名,可以使用一个简单记录日志的方法:this.getClass().getSimpleName()@这是一种在运行时捕获错误并告诉用户不允许使用该远程设备更改频道的方法。我可以随意修改代码。@sunrise76因为现在的情况是,任何遥控器都可以改变任何电视的频道,这不是它应该的方式。好吧,我明白了。因此,使用泛型和标记接口,您可以在编译时定义类应该使用什么。如果我试图在运行时使用三星电视和索尼遥控器,那么我会得到一个错误,因为这两种类型不应该一起工作,正如编译时定义的那样。对吗?差不多。事实上,很好的一点是,你不能尝试使用三星电视和索尼遥控器,它甚至不会编译。尝试取消注释行
//r.changeChannel(t1,37)然后编译-你应该会得到一个错误。啊,好的,我明白了。感谢您编译时安全(更具体地说是“类型安全”)通常比运行时检查更受欢迎。如果你相信
public static <M extends Manufacturer> void doSomethingWithTVOfAnyMake(Television<M> tv){
        int myChannel = tv.getChannel();
        //do more stuff...
    }