如何区分具有不同引用的同一类对象?

huangapple go评论69阅读模式
英文:

How to distinguish between objects of the same class with different references?

问题

抱歉,标题可能表达得不太好,我不太知道该如何提问。但我想区分相同类的实例,但被引用为不同的类。请考虑以下代码:

class Shape {}

class Circle extends Shape {}

class Main {
    public static void main(String[] args) {
        Circle myCircle = new Circle();
        Shape myOtherCircle = new Circle();

        System.out.print(myCircle.getClass() + ", ");
        System.out.println(myOtherCircle.getClass());

        System.out.print((myCircle instanceof Circle) + ", ");
        System.out.println(myOtherCircle instanceof Circle);

        System.out.print((myCircle instanceof Shape) + ", ");
        System.out.println(myOtherCircle instanceof Shape);

        System.out.print(Circle.class.isInstance(myCircle) + ", ");
        System.out.println(Circle.class.isInstance(myOtherCircle));

        System.out.print(Shape.class.isInstance(myCircle) + ", ");
        System.out.println(Shape.class.isInstance(myOtherCircle));
    }
}

我们可以通过上述方法或运算符按实例的类型区分对象,但正如所示,尝试根据引用的类型比较对象时,没有区别,代码输出如下:

class Circle, class Circle
true, true
true, true
true, true
true, true

如何通过类型引用区分myCirclemyOtherCircle?感谢阅读,我对所有的回答表示感谢。

英文:

Sorry if the title is poorly worded, I don't really know how to ask this. But I want to distinguish between instances of the same class, but referenced as different classes. Please consider following code:

class Shape {}

class Circle extends Shape {}

class Main {

public static void main(String[] args) {

    Circle myCircle = new Circle();
    Shape myOtherCircle = new Circle();

    System.out.print(myCircle.getClass() + ", ");
    System.out.println(myOtherCircle.getClass());

    System.out.print((myCircle instanceof Circle) + ", ");
    System.out.println(myOtherCircle instanceof Circle);

    System.out.print((myCircle instanceof Shape) + ", ");
    System.out.println(myOtherCircle instanceof Shape);

    System.out.print(Circle.class.isInstance(myCircle) + ", ");
    System.out.println(Circle.class.isInstance(myOtherCircle));

    System.out.print(Shape.class.isInstance(myCircle) + ", ");
    System.out.println(Shape.class.isInstance(myOtherCircle));
}

}

We can distinguish objects by the type of their instance by using the methods or operators shown above, but as shown, when trying to compare objects by the type of the reference there are no differences the code prints this:

class Circle, class Circle
true, true
true, true
true, true
true, true

How can I distinguish myCircle and myOtherCircle by the type reference. Thank you for reading, I appreciate all answers.

答案1

得分: 2

我认为这是不可能的。你能够最接近的方式是将这些变量作为类的字段。然后你可以通过类定义来访问类型:

class Main {
    Circle myMainCircle = new Circle();
    Shape myMainOtherCircle = new Circle();
		
    static class Shape {
    }
    
    static class Circle extends Shape {
    }
    
    public static void main(String[] args) throws Exception {			 
        System.out.println(Main.class.getDeclaredField("myMainCircle").getGenericType());
        System.out.println(Main.class.getDeclaredField("myMainOtherCircle").getGenericType());
    }
}

输出结果:

class Main$Circle
class Main$Shape
英文:

I don't think that is possible. The closest you can get is if these variables are fields of a class. Then you can access the type via the class definition:

class Main {
	Circle myMainCircle = new Circle();
	Shape myMainOtherCircle = new Circle();
		
	static class Shape {
	}
	
	static class Circle extends Shape {
	}
	
	public static void main(String[] args) throws Exception {			 
        System.out.println(Main.class.getDeclaredField("myMainCircle").getGenericType());
        System.out.println(Main.class.getDeclaredField("myMainOtherCircle").getGenericType());
	}
}

output:

class Main$Circle
class Main$Shape

答案2

得分: 0

我看到的问题是:“在什么情况下您不会知道引用类型?” 例如,我们可以创建两个方法。

public int special(Circle a){
    return 1;
}

public int special(Shape a){
    return 2;
}

然后使用您的示例。

System.out.println(special(myCircle) + "," + special(myOtherCircle));

(这将打印出 1,2,因为 Java 将使用最特定的方法。myCircle 是一个 Circle 和一个 Shape,但 Circle 是最特定的。)

然而,为了使此示例起作用,我们已经知道一个类被引用为 Shape,另一个类被引用为 Circle。

英文:

The problem I see is "When would you not know the reference type?" For example we could make two methods.

public int special(Circle a){
    return 1;
}

public int special(Shape a){
    return 2;
}

Then using your example.

System.out.println(special(myCircle) + ", " + special(myOtherCircle));

(This will print 1,2 because java will use the most specified method. myCircle is a Circle and a Shape, but the Circle is the most specified.)

For this to work though, we already know that one class is referenced as a Shape and the other a Circle.

答案3

得分: 0

换句话说,您想要在运行时获得对变量的引用的声明类型(而不是类字段——因为您可以使用内省来检查这些内容,正如Conffusion的答案中所示)

为什么需要在运行时进行检查?在什么情况下等到那时才有用?编译器早在编译时就已经知道,因为它会在编译时跟踪所有标识符的声明类型。在您的代码中,如果您编写:

Shape myOtherCircle = new Circle();
// ...
Circle c = myOtherCircle;  // 编译时错误:无效的隐式转换

这在编译时警告您正在做一些可疑的事情——因为编译器不允许隐式(非显式,也就是没有显式的(Circle)转换)的缩小转换。例如:从Shape隐式转换为Circle:是不好的,因为您可能会尝试将Square-Shape转换为Circle,这会导致运行时错误。从Circle转换为Shape,是一种"扩展"转换,不会出现错误。

因此,我的简短回答是:

> 您不能在运行时执行此操作,因为编译器(和您的IDE)在编译时已经具有此信息。

另一方面,对于计算机而言,几乎任何事情都是可能的,尽管有些事情相当复杂。通过使用JDK内置的Java编译器,可以在运行时检测出这种问题,以便对任何Java代码中的变量的声明类型进行编译和报告,但这样做当然不是预期的行为(大多数使用它的人只想要在运行时编译和运行代码,而不是玩弄AST),这需要深入研究内部结构。除了JDK自己的编译器之外,您还可以使用任何大量的Java编译器来执行类似的操作(但要注意可能与标准编译器存在的不兼容性)。

英文:

In other words, you want to know, at runtime, the declared type of a reference to a variable (and not a class field - since you can use introspection to check out those, as shown by Conffusion's answer)

Why would you need to check it at runtime? In what case could it be useful to wait until then? The compiler knows much earlier -- at compile time, as it keeps track of the declared types of all identifiers. In your code, if you write

 Shape myOtherCircle = new Circle();
 // ...
 Circle c = myOtherCircle;  // compile-time error: invalid implicit cast

This warns you, at compile-time, that you are doing something fishy - as the compiler does not allow implicit (= non-explicit, that is, without an expliccit (Circle) cast) narrowing casts. For example: implicit casting from a Shape to a Circle: bad, because you could try to convert a Square-Shape to a Circle which would lead to run-time errors. From a Circle to a Shape, a broadening cast, no errors can occur.

So, my short answer would be:

> you cannot do this at run-time because the compiler (and your IDE) already has this information at compile-time

On the other hand, with computers, almost everything is possible, although some are quite complicated. It is possible to detect such problems at runtime by using the JDK's built-in java compiler to (uh) compile and report on the declared types of variables in any piece of java code - but doing so is certainly not expected (most folks using it just want to compile and run code at runtime, rather than play with the AST), and requires a deep dive into internals. Asides from the JDK's own compiler, you can also use any of a large set of java compilers to do something similar (but beware possible incompatibilities with the standard one).

答案4

得分: 0

在Java中,Circle的实例是一个Circle,无论您是否将对它的引用存储在声明为CircleShapeObject的变量中。

因此,您对变量中的实例所执行的任何操作仅取决于实例的类,而不取决于变量的声明类型。这适用于诸如instanceof运算符、getClass()方法等的操作。

有一个例外情况:如果您有一些重载的方法,例如

String myType(Object x) { return "Object"; }
String myType(Shape x)  { return "Shape";  }
String myType(Circle x) { return "Circle"; }

那么编译器将根据在编译时已知的类型来决定调用哪个版本。如果您将一个变量传递给myType()的调用,编译器所假定的类型将是变量的类型,它不知道以后将在变量中引用的实例的类。

因此,下面的代码片段可能会实现您想要的效果:

System.out.print(myType(myCircle) + ", ");
System.out.println(myType(myOtherCircle));

但是,对于任何给定的变量,您在静态上知道您是如何声明它的,我不明白这样的结构可能有什么用处。

英文:

In Java, a Circle instance is a Circle, no matter if you store a reference to it in a variable declared as e.g. Circle, Shape or Object.

So, anything you do with the instance found in a variable only depends on the instance's class, not on the variable's declared type. That applies to things like the instanceof operator, the getClass() method and so on.

There's one exception: if you have some overloaded methods like

String myType(Object x) { return "Object"; }
String myType(Shape x)  { return "Shape";  }
String myType(Circle x) { return "Circle"; }

then the compiler will decide which version to call, based on the type as it is known at compile-time. And if you pass a variable into a call of myType(), the type that the compiler assumes will be the variable's type, not knowing about the class of the instance that will later be referenced in the variable.

So then the following snippet might do what you want:

System.out.print(myType(myCircle) + ", ");
System.out.println(myType(myOtherCircle));

But, as for any given variable you statically know how you declared it, I don't see how such a construct might be useful.

huangapple
  • 本文由 发表于 2020年9月30日 14:00:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/64131714.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定