Java方法重载混乱

时间:2013-12-22 18:10:57

标签: java inheritance interface overloading

在服务器代码中,我有一个ProtocolHandler类,它从套接字读取,找出它处理的数据包类型并将其发送给客户端。我试图拥有以下架构:

public interface Packet {
    //...
}

public class ClientInformations implements Packet {
    //...
}

public class ProtocolHandler {
    //....
    public void bytesReceived(byte[] bytes) {
        //... Determine the type of the packet
        Packet packet = determineTypeOfPacketAndRead(bytes);

        // Here  I already have the packet object built,
        // like ClientInformations@1a1a1a1a[...]

        client.packetReceived(packet);
    }
    //...
}

public class Client {
    //...
    public void packetReceived(Packet pkt) { 
        System.out.println("Unimplemented packet received.");
    }

    public void packetReceived(ClientInformations ci) {

    }

    //...
}

(我不知道如何用文字轻松解释)。问题是,我真的认为packetReceived(ClientInformations)会被调用,但事实并非如此。它调用了更通用的packetReceived(Packet)方法。我错了吗?那我怎么还能使用相同的架构呢?

- 编辑

现在我明白为什么会这样。问题是,我有其他数据包类,例如MovementSyncSpawn。我想通过向Client类添加新方法来轻松添加新数据包。那不是另一种做法吗?我的意思是,一种自动分析运行时packet对象的类型并进行最具体的方法调用的方法?

6 个答案:

答案 0 :(得分:5)

我假设你有Packet packet = determineTYpeOfPacketAndRead(bytes),因为代码几乎没有其他方式可以编译。

所以引用类型是Packet(在编译时),即使对象类型是ClientInformations(在运行时)。

并在Java语言规范第15章中指定方法重载

  

在编译时选择最具体的方法;它的描述符决定了在运行时实际执行的方法。

更新:您的代码目前正常运行,因为您的代码中没有歧义,即使它的行为不符合您的要求。但是,您将无法扩展此代码,因为它会导致问题,让我说明一下:

我之前提出的建议如下:

public class Client {
    //...
    public void unknownPacketReceived(Packet pkt) { 
        System.out.println("Unimplemented packet received.");
    }

    public void packetReceived(ClientInformations ci) {

    }

    //...
}

然而,这不能起作用,因为ClientInformations对象是一个更简单的Packet对象/接口,它会产生以下编译错误:

incompatible types: Packet cannot be converted to ClientInformations

引用类型仍为Packet时,会发生这种情况,而您希望将其提升为ClientInformations

如果您将其称为:

,它可以工作
client.receivedPacket((ClientInformations)packet)

这对你的情况没有任何害处,因为在运行时它们属于同一类型,因此转换中没有任何东西丢失,这需要伴随instanceof树,这就是你回来的原因现在是零步。

这个程序只是才能正常工作,如果你希望它变得简单,我建议改变你的设计,继续这条路径可能是一个选项,如果您正在处理遗留应用程序,并且不能只是更改内容,但这并不容易。

答案 1 :(得分:1)

问题是,如果我理解正确的话,你传递的对象都是PacketClientInformations,所以这两种方法都没有优先于其他方法。

只需给另一个方法另一个名称,你的问题就会解决。

答案 2 :(得分:1)

您必须按照以下方式执行此操作:

public void bytesReceived(byte[] bytes) {
    //... Determine the type of the packet
    packet = determineTypeOfPacketAndRead(bytes);

    // Here  I already have the packet object built,
    // like ClientInformations@1a1a1a1a[...]

    if (packet instanceof ClientInformations) {
        ClientInformations ci = (ClientInformations)packet;
        client.packetReceived(ci); // this way, it's clear which method you want to call
    }
}

但是,我更喜欢Tim B的建议来改变方法名称。我建议clientInformationsPacketReceived()

答案 3 :(得分:1)

Java是一种单一调度语言,所以诀窍是学会利用它来达到最佳效果。

在您的情况下,您不需要在Client类型上发送,但您确实需要Packet类型。让此要求指导您的设计:不要将方法重载添加到Client,而是让数据包包含像

这样的方法中的处理代码
void handle(Client c)

并将客户端的所有内容都提供给Packet。这将使您有机会在Packet的每个专业化中提供不同的处理。

不幸的是,调度的类型必须是拥有相关方法的类型,这可能会损害您分离问题的需要。这可以通过更多的架构来实现,但我希望这个指南可以帮助你。

答案 4 :(得分:1)

为什么的答案已在其他答案中显示。一个解决方案(据我所知)需要改变你的思维过程。

现在你正在尝试将逻辑放在某种通用类中来处理数据包,该类可以处理每种类型的数据包。这是一种相当常见的反模式:创建“管理器”类,为特定对象完成工作。

一个简单的解决方案就是将这个逻辑简单地放在你的课程中。

当您遇到以下情况时

interface Packet {
    void packetReceived();
}

class ClientInformations implements Packet {
    @Override
    public void packetReceived() {
        System.out.println("I am ClientInformations");      
    }
}

您可以在变量packetReceived上致电packet,这样就可以为您完成工作。

答案 5 :(得分:0)

您可以typecast您的对象引用来调用packetReceived(ClientInformations)而不是packetReceived(Packet)

你可以这样打电话client.packetReceived((ClientInformations)packet);