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
我想到了三种解决方案,其中两种解决方案在这种情况下不起作用
-
Make
Literal
the base class and all others its children, butLiteral
does not have all methods that its children will have and there would be many children.将Literal作为基类,让所有其他人成为它的子类,但是Literal没有它的孩子将拥有的所有方法,并且会有很多孩子。
-
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不允许在泛型上调用任何方法。
-
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());
}