2016-02-13 60 views
0

我有一個非常簡單的包裝類,它存儲任何類及其類型的對象。現在我想要檢索該對象並想對其執行一些操作。在Java中動態類型轉換對象

這是我目前使用我的代碼,一個簡短的演示,但它變得太長

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*/ 

三種解決方案來到我的腦海從兩者沒有在這種情況下工作

  1. 使Literal基類和其他所有其他孩子,但Literal並沒有其子女將擁有的所有方法,會有很多孩子。

  2. 使Literal是一個泛型類,但我想要存儲的類型是可變的,所以我需要複製它們,Java不允許調用泛型的任何方法。

  3. 這是行得通的,但它增加了很難找到的錯誤的概率。

這是迄今爲止我嘗試:

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(); 
    } 
} 

在過去的方法可能錯誤是:

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(); 
    } 
} 

我已經注意到這一個,但可能有更多的錯誤。我應該重新考慮我的 數據結構嗎?

我想知道是否有任何簡單,不易出錯,高效和簡短的方法來做到這一點。

+0

*難以發現的錯誤*後[MCVE(http://stackoverflow.com/help/mcve) –

+0

這裏是最後的4行代碼的問題? –

+0

@RomanC我添加了MCVE –

回答

0

你一定要使用泛型來實現你的目標! 如果你需要不同的值的不同構造函數,我會建議使用靜態方法做初始化部分,並使構造函數爲私有。我好像在你的情況下工作..

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 
} 

現在,您可以撥打:

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

在一個側面說明..嘗試使用開關的情況下爲您的枚舉。它們比一百個塊要快得多,可以說不那麼難看,並且總是大寫枚舉的名稱,因爲它們基本上是最終的常量。 This tutorial site由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

這是教科書案例爲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. 
} 

簡而言之,讓類型系統幫助您使用方法調度;不需要重新實現它。

0

我發佈此作爲新的答案,因爲您更新了您的問題,我可以提供一個替代解釋爲什麼您的代碼示例不會產生您的想法(而舊的帖子仍然有效): 您的兩個類RealComplex2的繼承方式錯誤。考慮下面的例子:

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 

那麼,爲什麼繼承是錯誤的?我們知道實數是複數的一個子集。所以每個實數總是一個複數,而不是相反!讓Real延長Complex2並創建一個構造函數:

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

我聽說孩子應該被超級設置,並且父母應該是子集 –

+0

當'X'繼承'Y'時總是問自己:每個'X'a'Y'? 'Complex2'和'Real'類不能通過這個測試,因爲:每個複數都是實數嗎?沒有!你可以在這裏找到類似的問題:http://stackoverflow.com/questions/28963147/inheritance-hierarchy-for-number-system-classes –

+0

並請考慮upvoting任何有用的答案 –