Java中对象的动态类型转换

时间:2022-08-06 16:26:28

I have a very simple wrapper class which stores object of any class and their type. Now I want to retrieve that object and want to perform some actions on it.

我有一个非常简单的包装类,它存储任何类的对象及其类型。现在我想要检索该对象并希望对其执行某些操作。

This is a short demo from my code which I am currently using, but it is getting too long

这是我目前正在使用的代码的简短演示,但它太长了

Literal a = new Literal(new Complex(12, 0));
Literal b = new Literal(new Matrix(n, m));
Literal c = new Literal(new Variable("x"));
Literal d = new Literal(new Constant("y", 2.25));
Literal e = new Literal(new Real(2.5));

if (a.getType() == Literal.Type.COMPLEX)
{
    Complex w = (Complex)a.getLiteral();
    //Doing something
}
else if (a.getType() == Literal.Type.Matrix)
{
    Matrix w = (Matrix)a.getLiteral();
    //Doing something
}
else if (a.getType() == Literal.Type.Variable)
{
    Variable w = (Variable)a.getLiteral();
    //Doing something
}
else if (a.getType() == Literal.Type.Constant)
{
    Constant w = (Constant)a.getLiteral();
    //Doing something
}
else if (a.getType() == Literal.Type.Real)
{
    Real w = (Real)a.getLiteral();
    //Doing something
}

/* Same goes for all other Objects
* and I need to do this at least in
* 50 different places*/

Three solutions came to my mind from which two are not working in this situation

我想到了三种解决方案,其中两种解决方案在这种情况下不起作用

  1. Make Literal the base class and all others its children, but Literal does not have all methods that its children will have and there would be many children.

    将Literal作为基类,让所有其他人成为它的子类,但是Literal没有它的孩子将拥有的所有方法,并且会有很多孩子。

  2. Make Literal a generic class, but the types I want to store are mutable so I need copy them and Java does not allow to call any method on generics.

    使Literal成为泛型类,但我想要存储的类型是可变的,所以我需要复制它们,Java不允许在泛型上调用任何方法。

  3. This is working, but it increases the probability of errors which are hard to find.

    这是有效的,但它增加了很难找到的错误概率。

Here is my attempt so far:

这是我到目前为止的尝试:

import org.apache.commons.math3.linear.BlockRealMatrix;

public class Literal<T>
{
    private final Object literal;

    /*
    * public enum Type
    * {
    *   COMPLEX,
    *   VARIABLE,
    *   REAL,
    *   CONSTANT,
    *   MATRIX
    * }
    * private final Type type;
    * */

    public Literal(Complex c)//Complex class is mutable so make copy of passed object
    {
        this.literal = new Complex(c);
        //this.type = Type.COMPLEX;
    }

    public Literal(Real d)//Real is not mutable so no need to make copy
    {
        this.literal = d;
        //this.type = Type.REAL;
    }

    public Literal(BlockRealMatrix realMatrix)//BlockRealMatrix class is mutable so make copy of passed object
    {
        int m = realMatrix.getData().length;
        int n = realMatrix.getData()[0].length;

        BlockRealMatrix mat = new BlockRealMatrix(m, n);

        for (int i = 0; i < m; i++)mat.setRow(i, realMatrix.getRow(i));
        this.literal = mat;

        //this.type = Type.MATRIX;
    }

    public T get()
    {
        return (T)this.literal;
    }

    /*
    public Literal.Type getType()
    {
        return this.type;
    }
    */

    @Override
    public String toString()
    {
        return this.literal.toString();
    }
}

For the last approach possible error is:

对于最后一种方法,可能的错误是:

package com.kmstudios.evaluator;

public class Main
{
    public static void main(String[] args)
    {
        Literal<Real> a = new Literal<>(new Complex2(25, 36));//Accidently passed reference of Real instead of Complex
        Literal<Complex2> b = new Literal<>(new Complex2(50, 36));

        Literal<Complex2> c = new Literal<>(a.get().multiply(b.get()));
        System.out.println(c.toString());//Expecting for Complex to print
    }
}

class Real
{
    private final Double d;

    public Real(double d)
    {
        this.d = d;
    }

    public Real multiply(Real other)
    {
        return new Real(this.d * other.d);
    }

    public final double get()
    {
        return this.d;
    }

    @Override
    public String toString()
    {
        return Double.toString(this.d);
    }
}

class Complex2 extends Real
{
    private double imaginary;

    public Complex2(double real, double imaginary)
    {
        super(real);
        this.imaginary = imaginary;
    }

    public Complex2 multiply(Complex2 o)
    {
        return new Complex2(this.get() * o.get() - this.imaginary * o.imaginary, this.get() * o.imaginary + this.imaginary * o.get());
    }

    public Complex2 multiply(double other)
    {
        return new Complex2(this.get() * other, this.imaginary * other);
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder("");
        builder.append(this.get());

        if (this.imaginary >= 0.0)builder.append("+");
        builder.append(this.imaginary);

        return builder.toString();
    }
}

I have noticed this one but there may be more errors. Should I reconsider my data structures?

我注意到了这个,但可能会有更多错误。我应该重新考虑我的数据结构吗?

I want to know if there is any simple, less error prone, efficient and short way to do this.

我想知道是否有任何简单,不易出错,高效和简短的方法来做到这一点。

3 个解决方案

#1


1  

This is the textbook case for the Visitor pattern.

这是访客模式的教科书案例。

public abstract class Literal<T extends Literal<T>> {
   public abstract void accept(Visitor visitor);
   // other elements
}

public class Variable extends Literal<Variable> {
   public void accept(Visitor visitor) {
      visitor.visit(this);
   }
   // other elements
}
public class Constant extends Literal<Constant> {
   public void accept(Visitor visitor) {
      visitor.visit(this);
   }
   // other elements
}

public interface Visitor {
   public void visit(Variable variable);
   public void visit(Constants variable);
   // ... other types
}

public class DoSomethingVisitor implements Visitor{
   public void visit(Variable variable) {
      // do something with a variable
   }
   public void visit(Constant constant) {
      // do something with a constant
   }
   // other methods.
}

Simply put, let the type system help you with method dispatch; no need to reimplement it.

简单地说,让类型系统帮助您进行方法调度;无需重新实现它。

#2


0  

I am posting this as a new answer, since you updated your question and I can provide an alternative explanation as to why your code example doesn't produce what you had in mind (while the old post is still valid): Your two classes Real and Complex2 have the inheritance the wrong way round. Consider the following example:

我发布这个作为一个新的答案,因为你更新了你的问题,我可以提供一个替代解释,为什么你的代码示例不会产生你的想法(虽然旧的帖子仍然有效):你的两个类真实而Complex2的继承方式是错误的。请考虑以下示例:

Complex2 complex = new Complex2(2, 3);
Real real = new Real(3);
Real result = real.multiply(complex); // returns an object equal to new Real(6)
Real result = complex.multiply(real); // and even this code returns the same result

So why is the inheritance wrong? We know that the real numbers are a subset of the complex numbers. So every real number is always also a complex number, not the other way round! Make Real extend Complex2 and create a constructor:

那么为什么遗产错了呢?我们知道实数是复数的一个子集。所以每个实数总是一个复数,而不是相反! Make Real扩展Complex2并创建一个构造函数:

public Real(double number){
    super(number, 0);
}

#3


0  

You should definitely use generics to achieve your goal! If you need different constructors for the different values, I would suggest using static methods to do the initialisation part and make the constructor private. I seem to work in your case..

你绝对应该使用泛型来实现你的目标!如果您需要不同值的不同构造函数,我建议使用静态方法来执行初始化部分并使构造函数成为私有。我似乎在你的情况下工作..

public class Literal<T>
{
    private final T literal;

    private Literal(T literal) {
        this.literal = literal;
    }

    public T get(){
        return literal;
    }

    public static Literal<Complex> createComplexLiteral(Complex complex){
        return new Literal<>(complex);
    }

    public static Literal<BlockRealMatrix> createBlockRealMatrixLiteral(BlockRealMatrix complex){
        int m = realMatrix.getData().length;
        int n = realMatrix.getData()[0].length;

        BlockRealMatrix mat = new BlockRealMatrix(m, n);

        for (int i = 0; i < m; i++)mat.setRow(i, realMatrix.getRow(i));
        return new Literal<>(mat);
    }

    // all the other create methods
}

You could now call:

你现在可以打电话:

Literal<Complex> complexLiteral = Literal.createComplexLiteral(someComplex);

On a side note.. Try using a switch case for your enums. They are much faster and arguably less ugly than a hundred if elseif blocks And always capitalize names of enums, since they are basically final constants. This tutorial site by oracle itself suggests this naming convention:

在侧面说明..尝试使用开关盒为您的枚举。它们比一百个if ifif块更快,可以说更难看。并且总是大写枚举的名称,因为它们基本上是最终常量。 oracle本教程网站本身提出了这个命名约定:

switch(a.getType()){
    case COMPLEX:
        Complex w = (Complex)a.getLiteral();
        //Doing something
        break;
    case MATRIX:
        Matrix w = (Matrix)a.getLiteral();
        //Doing something
        break;
    case VARIABLE:
        Variable w = (Variable)a.getLiteral();
        //Doing something
        break;
    // other types..
    default: // always include a default for your switch case, just in case..
        throw new RuntimeException("unknown type "+a.getType());
}

#1


1  

This is the textbook case for the Visitor pattern.

这是访客模式的教科书案例。

public abstract class Literal<T extends Literal<T>> {
   public abstract void accept(Visitor visitor);
   // other elements
}

public class Variable extends Literal<Variable> {
   public void accept(Visitor visitor) {
      visitor.visit(this);
   }
   // other elements
}
public class Constant extends Literal<Constant> {
   public void accept(Visitor visitor) {
      visitor.visit(this);
   }
   // other elements
}

public interface Visitor {
   public void visit(Variable variable);
   public void visit(Constants variable);
   // ... other types
}

public class DoSomethingVisitor implements Visitor{
   public void visit(Variable variable) {
      // do something with a variable
   }
   public void visit(Constant constant) {
      // do something with a constant
   }
   // other methods.
}

Simply put, let the type system help you with method dispatch; no need to reimplement it.

简单地说,让类型系统帮助您进行方法调度;无需重新实现它。

#2


0  

I am posting this as a new answer, since you updated your question and I can provide an alternative explanation as to why your code example doesn't produce what you had in mind (while the old post is still valid): Your two classes Real and Complex2 have the inheritance the wrong way round. Consider the following example:

我发布这个作为一个新的答案,因为你更新了你的问题,我可以提供一个替代解释,为什么你的代码示例不会产生你的想法(虽然旧的帖子仍然有效):你的两个类真实而Complex2的继承方式是错误的。请考虑以下示例:

Complex2 complex = new Complex2(2, 3);
Real real = new Real(3);
Real result = real.multiply(complex); // returns an object equal to new Real(6)
Real result = complex.multiply(real); // and even this code returns the same result

So why is the inheritance wrong? We know that the real numbers are a subset of the complex numbers. So every real number is always also a complex number, not the other way round! Make Real extend Complex2 and create a constructor:

那么为什么遗产错了呢?我们知道实数是复数的一个子集。所以每个实数总是一个复数,而不是相反! Make Real扩展Complex2并创建一个构造函数:

public Real(double number){
    super(number, 0);
}

#3


0  

You should definitely use generics to achieve your goal! If you need different constructors for the different values, I would suggest using static methods to do the initialisation part and make the constructor private. I seem to work in your case..

你绝对应该使用泛型来实现你的目标!如果您需要不同值的不同构造函数,我建议使用静态方法来执行初始化部分并使构造函数成为私有。我似乎在你的情况下工作..

public class Literal<T>
{
    private final T literal;

    private Literal(T literal) {
        this.literal = literal;
    }

    public T get(){
        return literal;
    }

    public static Literal<Complex> createComplexLiteral(Complex complex){
        return new Literal<>(complex);
    }

    public static Literal<BlockRealMatrix> createBlockRealMatrixLiteral(BlockRealMatrix complex){
        int m = realMatrix.getData().length;
        int n = realMatrix.getData()[0].length;

        BlockRealMatrix mat = new BlockRealMatrix(m, n);

        for (int i = 0; i < m; i++)mat.setRow(i, realMatrix.getRow(i));
        return new Literal<>(mat);
    }

    // all the other create methods
}

You could now call:

你现在可以打电话:

Literal<Complex> complexLiteral = Literal.createComplexLiteral(someComplex);

On a side note.. Try using a switch case for your enums. They are much faster and arguably less ugly than a hundred if elseif blocks And always capitalize names of enums, since they are basically final constants. This tutorial site by oracle itself suggests this naming convention:

在侧面说明..尝试使用开关盒为您的枚举。它们比一百个if ifif块更快,可以说更难看。并且总是大写枚举的名称,因为它们基本上是最终常量。 oracle本教程网站本身提出了这个命名约定:

switch(a.getType()){
    case COMPLEX:
        Complex w = (Complex)a.getLiteral();
        //Doing something
        break;
    case MATRIX:
        Matrix w = (Matrix)a.getLiteral();
        //Doing something
        break;
    case VARIABLE:
        Variable w = (Variable)a.getLiteral();
        //Doing something
        break;
    // other types..
    default: // always include a default for your switch case, just in case..
        throw new RuntimeException("unknown type "+a.getType());
}