Java 对象模型和类型识别方法

发布于 2024-11-26 01:35:58 字数 691 浏览 6 评论 0原文

我正在编写一个应用程序,其中有一些代表世界中事物的类。

世界由对象数组表示(世界中存在的所有对象类都继承自该类 - 我们称之为 Thing)。

class World {
  Thing[] myObjects;
}

class Thing {}
class AAA extends Thing {}
class BBB extends Thing {}

在某些时候,我需要知道给定位置的对象是否属于给定类型。

我有一些解决方案,并且想与熟悉 Java 对象模型的人讨论每个解决方案的优点,因为我习惯了与 Java (CLOS) 不同的对象模型。

解决方案#1

在 World 类中定义方法 isAThingAAA(obj) 和 isAThingBBB(obj)。

这些方法将调用 obj.getClass() 并检查返回的类型是 AAA 还是 BBB。

我看到的问题是必须使用“getClass”来实现它。或者还有其他方法来实现吗?

解决方案#2

在 Thing 类中定义方法 isAnAAA () 和 isAnBBB (),实现返回 false。在各自的类(AAA.isAnAAA 和 BBB.isAnBBB)中重新定义它们以返回 true。

这听起来很奇怪,因为最抽象的类会知道其子类的存在。

其他建议?

提前致谢。

I'm writing an app that has a few classes which represent things that are in a World.

The World is represented by an array of objects (of the class from which all classes of objects that exist in the world inherit - lets call it Thing).

class World {
  Thing[] myObjects;
}

class Thing {}
class AAA extends Thing {}
class BBB extends Thing {}

At some point I need to know if an object at a given position is of a given type.

I have some solutions and would like to discuss the merits of each with people who are familiar with Java's object model, since I'm used to a different object model than Java's (CLOS).

Solution #1

Define methods isAThingAAA(obj) and isAThingBBB(obj) in the World class.

These methods would call obj.getClass () and check if the returned type is AAA or BBB.

The problem I see with this is having to use "getClass" to implement it. Or is there another way to implement it?

Solution #2

Define methods isAnAAA () and isAnBBB () in the Thing class, implemented to return false. Redefine them in the respective class (AAA.isAnAAA and BBB.isAnBBB) to return true.

This sesms strange because the most abstract class would have knowledge of the existance of its subclasses.

Other suggestions?

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

故人爱我别走 2024-12-03 01:35:59

您可以使用instanceof或isInstance http://download.oracle.com/javase/6/docs/api/java/lang/Class.html#isInstance%28java.lang.Object%29

class World {
    Thing[] myObjects;
}
class Thing {}
class AAA extends Thing {}
class BBB extends Thing {}
public class Main {
    public static void main(String[] args) {
        Thing[] things={new AAA(),new BBB()};
        for(Thing thing:things)
            if(thing instanceof AAA)
                System.out.println("i am an AAA");
            else if(thing instanceof BBB)
                    System.out.println("i am a BBB");
        }
}

you can use instanceof or isInstance http://download.oracle.com/javase/6/docs/api/java/lang/Class.html#isInstance%28java.lang.Object%29

class World {
    Thing[] myObjects;
}
class Thing {}
class AAA extends Thing {}
class BBB extends Thing {}
public class Main {
    public static void main(String[] args) {
        Thing[] things={new AAA(),new BBB()};
        for(Thing thing:things)
            if(thing instanceof AAA)
                System.out.println("i am an AAA");
            else if(thing instanceof BBB)
                    System.out.println("i am a BBB");
        }
}
女中豪杰 2024-12-03 01:35:59

最简单的方法是使用 Java 的内置 instanceof 运算符。例如:

public boolean isAAA(Thing myThing)
{
  return myThing instanceof AAA;
}

然而,许多人会告诉您,使用instanceof是类设计不佳的症状,而用不同的子类实现不同行为的正确方法是通过多态性。这里的问题是,在 Java 中,让一个类根据它的派生类型做不同的事情是很容易的,但是让其他对象根据它具有句柄的派生类型来不同地处理一个 Thing 就有点棘手了在。这是双重调度问题。

如果在处理 Thing 对象时,您可以将问题分派给其他一些方法,这些方法会根据 Thing 的子类对它执行不同的操作,那就太理想了,如下所示:

public void handleThing(Thing myThing)
{
  reactToThing(myThing);
}

public void reactToThing(AAA myThing)
{
  // Do stuff specific for AAA
}

public void reactToThing(BBB myThing)
{
  // Do stuff specific for BBB
}


public void reactToThing(Thing myThing)
{
  // Do stuff for generic Thing
}

然而,在 Java 中,它只支持单分派,无论 handleThing() 中 myThing 的实际类型如何,reactToThing(Thing) 都将始终被调用,并且您永远不会获得独特的行为。

要解决此问题,您需要做的是使用访问者模式。这仅涉及在您的 Thing 类及其所有子类中添加一些额外的代码,以便为您的reactToThing() 方法提供一些额外的上下文。假设上述方法都在一个名为 Visitor 的类中。我们可以重写上面的方法,首先将问题交给 Thing 对象本身,然后多态性将为我们提供适当的上下文(Thing、AAA 或 BBB),以便对象返回给访问者。

那么,我们可以将上面的例子重写如下:

public class Visitor
{
  // ...

  public void handleThing(Thing myThing)
  {
    myThing.accept(this);
  }

  public void reactToThing(AAA myThing)
  {
    // Do stuff specific for AAA
  }

  public void reactToThing(BBB myThing)
  {
    // Do stuff specific for BBB
  }


  public void reactToThing(Thing myThing)
  {
    // Do stuff for generic Thing
  }
}


public class Thing
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}


public class AAA
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}


public class BBB
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}

那么为什么我需要在两个子类中重写相同的accept()方法呢?因为否则当调用访问者.reactToThing(this) 方法时,对象仍然处于 Thing 上下文中,因此我们会遇到与之前相同的问题。通过在所有三个地方重新实现它,派生类将覆盖父类的实现,并在 Visitor 中调用正确的方法。

当您只想知道正在使用的派生类时,似乎需要做很多工作,但回报是可扩展性和维护性。现在你不需要到处添加 if (something instanceof SomethingElse) 。具体来说,如果您决定将来需要更改某些内容(例如扩展 Visitor),则需要维护的重复代码将减少。

希望能解决您的问题。

The simplest way would be to use Java's built-in instanceof operator. For example:

public boolean isAAA(Thing myThing)
{
  return myThing instanceof AAA;
}

Many people, however, would tell you that using instanceof is symptomatic of poor class design, and that the proper way to achieve different behavior with different subclasses is through polymorphism. The problem here is that it it's very easy in Java to get a class to do something different based on what derived type it is, but it's a bit tricky getting some other object to treat a Thing differently depending upon what derived type it has a handle on. This is the Double Dispatch problem.

It would be ideal if, when handling your Thing object, you could just dispatch the problem to some other methods that would do different things with the Thing depending on what subclass it is, like so:

public void handleThing(Thing myThing)
{
  reactToThing(myThing);
}

public void reactToThing(AAA myThing)
{
  // Do stuff specific for AAA
}

public void reactToThing(BBB myThing)
{
  // Do stuff specific for BBB
}


public void reactToThing(Thing myThing)
{
  // Do stuff for generic Thing
}

In Java, however, which only supports single dispatch, regardless of the actual type of myThing in handleThing(), reactToThing(Thing) will always get called, and you'll never get your unique behavior.

What you need to do to get around this problem is use the Visitor Pattern. This just involves putting some extra code in your Thing class and all its children to give your reactToThing() methods some extra context. So let's say the above methods are all in a class called Visitor. We can rewrite the above to work by first handing the problem off to the Thing object itself, then polymorphism will give us an appropriate context (Thing, AAA, or BBB) for the object to give back to the Visitor.

So, we can rewrite the example above as follows:

public class Visitor
{
  // ...

  public void handleThing(Thing myThing)
  {
    myThing.accept(this);
  }

  public void reactToThing(AAA myThing)
  {
    // Do stuff specific for AAA
  }

  public void reactToThing(BBB myThing)
  {
    // Do stuff specific for BBB
  }


  public void reactToThing(Thing myThing)
  {
    // Do stuff for generic Thing
  }
}


public class Thing
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}


public class AAA
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}


public class BBB
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}

So why did I need to rewrite the same accept() method in both subclasses? Because otherwise the object would still be in a Thing context when calling the visitor.reactToThing(this) method, and thus we have the same problem as before. By reimplementing it in all three places, the derived class overrides the parent's implementation, and the correct method is called in Visitor.

Seems like a lot of work when all you want to know is what derived class you're working with, but the payoff is extensibility and maintenance. Now you don't need to go around adding if (something instanceof somethingElse) all over the place. Specifically, you'll have less duplicate code you need to maintain if you ever decide you need to change something down the road, like extend Visitor, for example.

Hope that addresses your question.

毅然前行 2024-12-03 01:35:58

在 Thing 类中编写一个抽象方法,然后让 AAA 的实例和 BBB 的实例重新定义它怎么样?您似乎想编写 isAnXXX 方法,也许您可​​以解释原因。

请注意,使用 instance of 运算符和 isAnXXX 方法不会导致多态性。这不是一件好事。你想要多态性,你需要它......咕噜,咕噜。另外,考虑一下明天您想要向您的 World 添加一个 CCC 类。您的设计应保证 World 类不会被触及,如 开放/封闭原则

所以,总结一下,你可以这样做:

在类 Thing 中:

public abstract class Thing{
   abstract void doSomething();
}

然后在子类中重写它

public class AAA extends Thing{

@override
public void doSomething(){ /*do something in AAAs way*/}

}

public class BBB extends Thing{

@override
public void doSomething(){ /*do something in BBBs way*/}

}

然后你可以填充你的 Thing[] 并执行此操作

   for (Thing t:myOBjects){
       t.doSomething()

   }

的每个实例事物知道如何到 doSomething,而无需询问其类型。

How about writing an abstract method in class Thing, and then letting AAA's instances and BBB's instances redefine it. You seem to want to write the isAnXXX methods, maybe you could explain why.

See, using the instance of operator and isAnXXX methods can lead to no polymorphism. And that is not a good thing. You WANT polymorphism, you needs it...gollum,gollum. Also, consider that tomorrow you want to add a CCC class to your World. Your design should guarantee that the World class won't be touched, as in the Open/closed principle

So , summing up, you could do this:

In class Thing:

public abstract class Thing{
   abstract void doSomething();
}

Then override it in child classes

public class AAA extends Thing{

@override
public void doSomething(){ /*do something in AAAs way*/}

}

public class BBB extends Thing{

@override
public void doSomething(){ /*do something in BBBs way*/}

}

Then you could fill your Thing[] and do this

   for (Thing t:myOBjects){
       t.doSomething()

   }

Each instance of Thing knows how to doSomething, without having to ask for its type.

你是年少的欢喜 2024-12-03 01:35:58

另一种选择是根本不使用这些方法。这些方法通常用于决定要做什么。相反

if(thing is a AAA) {
   ((AAA) thing).method();
} else if (thing is a BBB) {
   ((BBB) thing).method();
}

,当需要采取行动时,每个事物都知道该做什么会更好。你应该打电话的是

thing.method(); // each type know what to do.

Another option is to not have these methods at all. These methods are often used to decide what to do. Something like

if(thing is a AAA) {
   ((AAA) thing).method();
} else if (thing is a BBB) {
   ((BBB) thing).method();
}

instead it is better for each Thing to know what to do when an action is required. All you should have to call is

thing.method(); // each type know what to do.
暮光沉寂 2024-12-03 01:35:58

您应该使用 instanceof 运算

AAA aaa = new AAA();
if(aaa instanceof AAA) {
    //do something different
}

符编辑:汤姆的回答也应该足以解决您的问题,这是很好的做法。

You should use instanceof operator

AAA aaa = new AAA();
if(aaa instanceof AAA) {
    //do something different
}

EDIT: also Tom's answer should suffice to solve your problem and that is the good practice.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文