Java中的动态类型转换

3
我有两个平行的类层次结构,我希望将一个类的对象转换为另一个类的对象。我可以通过手动指定要执行的转换来实现,但我需要在实际转换发生时和需要调用它时都要指定转换。有没有一种方法只需指定一次即可?
以下是示例代码,可以正常工作,但我想简化代码。这里有两个转换函数,允许我在输入类(bI,cI)和输出类(bO,cO)之间进行转换。虽然这是不可避免的,但instanceof比较让人感到不舒服。是否有一种优雅的解决方案可以避免它们?
public class ConversionTest {
    static public class aI {}
    static public class bI extends aI {}
    static public class cI extends aI {}

    static public class aO {}
    static public class bO extends aO {}
    static public class cO extends aO {}

    // Convert from bI to bO.
    private static aO convert(bI iVar) {return new bO();}
    // Convert from cI to cO.
    private static aO convert(cI iVar) {return new cO();}

    public static void main(String argv []) {
        // Input variable.
        final aI iVar = new bI();
        // Output variable.
        final aO temp;
        // Perform conversion.
        if(iVar instanceof bI) {
            temp = convert((bI)iVar);
        } else if (iVar instanceof cI) {
            temp = convert((cI)iVar);
        }
    }
}

我想要做类似这样的事情:
final a0 temp = convert(iVar.getClass().cast(iVar));

但是在这种情况下,编译器会报告找不到适当的转换函数。也许我可以指定一个包含所有可能转换的数组来尝试?

final a0 temp = convertHelp({bI,cI}, iVar);

我不确定该如何实现这个convertHelp函数。它将遍历数组并尝试找到适当的转换方式。有什么建议吗?

谢谢。


我故意不希望输入和输出类彼此知道。我同意每个人建议使用多态会解决问题,但我正在寻找另一种解决方案。在我的情况下,我无法控制输入/输出类,也无法扩展它们。 - sjcomp
6个回答

3

你是否可以访问类的实现?如果可以,你可以使用多态性来使转换在每个类内部执行正确的操作。

class ai
{
    public ao convert()
    {
        return new ao();
    }
}

class bi extends ai
{
    @Override
    public ao convert()
    {
        return new bo();
    }
}

class ci extends ai
{
    @Override
    public ao convert()
    {
        return new co();
    }
}

public static void main(String argv []) {      
// Input variable.      
final aI iVar = new bI();      
// Output variable.      
final aO temp = iVar.convert();
}

没错。请注意,自Java 5以来,您可以在覆盖时使用协变返回类型(即 @Override public co convert() { return new co(); })。 - meriton

1

鉴于您无法修改输入/输出类,最干净的解决方案可能是使用反射:

class Converter { 
   public ao convert(ai iObj) {
      final Method m = getClass().getDeclaredMethod("cvrt", iObj.getClass());
      return (ao)m.invoke(this, iObj); 
   }

   public ao cvrt(ai iObj) {
      return new ao();
   }

   public bo cvrt(bi iObj) {
      return new bo();
   }

   public co cvrt(ci iObj) {
      return new co();
   }
}

我一直在寻找一种能够提供更好类型安全性的东西,但是如果说有什么东西的话,我更喜欢这个解决方案,而不是手动说明。 - sjcomp

1

你已经实现的代码是正确的,而且比使用反射API得到的代码更漂亮。

可以实现ConvertHelp;它需要以Class对象数组作为参数。但是,ConvertHelp的主体会非常丑陋。

你正在违背静态类型语言的理念。Java并不是为了让这种方式变得容易 - 这种方式很容易产生不安全的代码。


我尝试使用Class对象,但无法使其工作。我不知道类列表的输入类型应该是什么。也许我需要使用原始类而不是泛型。 - sjcomp

0

如果您不希望输入类依赖于输出类,仍然可以从多态性中受益:

interface IFactory<T> {
   public T createA();
   public T createB();
   public T createC();
}

class ai
{
    public T convert(IFactory<T> f)
    {
        return f.createA();
    }
}

class bi extends ai
{
   @Override
   public T convert(IFactory<T> f)
   {
       return f.createB();
   }
}

class ci extends ai
{
   @Override
   public T convert(IFactory<T> f)
   {
       return f.createB();
   }
}

class FactoryO extends IFactory<ao> {
   public ao createA() {
      return new ao();
   }

   public bo createB() {
      return new bo();
   }

   public co createC() {
      return new co();
   }
}

执行转换操作:

FactoryO f = new FactoryO();
ai ivar = // ...
ao ovar = ivar.convert(f);

0
理想情况下,正如其他帖子建议的那样,您应该在输入类上拥有一个转换方法,以便它们自己可以产生输出类。如果您想要拥有并行的转换器层次结构,则最好使用Visitor模式。
您可以定义一个ConverterVisitor接口,并按以下方式拥有两个实现:
static public interface ConverterVisitor {
  public aO convert(aI iVar);
}

static public class bIConverter implements ConverterVisitor{
  public aO convert(aI iVar){
  // requires a cast from aI to bI
    return new bO();
  }
}

static public class cIConverter implements ConverterVisitor{
  public aO convert(aI iVar){
    // requires a cast from aI to cI
    return new cO();
  }
}

但是bI和cI类也需要可修改,这不是你想要的。

因此,唯一剩下的解决方案是使用ReflectionVisitor模式。

这将涉及编写一个类,该类将使用某种命名约定获取相应的转换器。在这种情况下,转换器的名称是使用将“Converter”附加到类名的约定获得的。因此,转换器工厂实现可能如下所示:

static public class ConverterFactory {

public aO convert(aI ai) throws Exception {
  return (aO) ((ConverterVisitor) Class.forName(ai.getClass().getName() + "Converter").newInstance()).convert(ai);
  }
}

非常丑陋!但它能工作!

第二种选择是在您的工厂中使用某种注册表。以下是完整列表。这种方法更好,因为它使用泛型来避免多次转换。

import java.util.HashMap;
import java.util.Map;

public class Trial {
        static public class aI {}
        static public class bI extends aI {}
        static public class cI extends aI {}

        static public class aO {}
        static public class bO extends aO {}
        static public class cO extends aO {}

        static public interface ConverterVisitor<T extends aI> {
            public aO convert(T iVar);
        }

        static public class bIConverter implements ConverterVisitor<bI>{

          public bIConverter(ConverterFactory converterFactory){
              converterFactory.register(bI.class, this);
          }

          public aO convert(bI iVar){
              return new bO();
          }
        }

        static public class cIConverter implements ConverterVisitor<cI>{

              public cIConverter(ConverterFactory converterFactory){
                  converterFactory.register(cI.class, this);
              }


             public aO convert(cI iVar){
                  return new cO();
             }
         }


        static public class ConverterFactory {
            public Map<Class<?>,ConverterVisitor<?>>converterRegistry = new HashMap<Class<?>, ConverterVisitor<?>>();

            public <T extends aI> void register(Class<T> clazz,ConverterVisitor<T> converter){
                converterRegistry.put(clazz,converter);
            }

            @SuppressWarnings("unchecked")
            public<T extends aI> aO convert(T ai) {
                return ((ConverterVisitor<T>)converterRegistry.get(ai.getClass())).convert(ai);
            }
        }


        public static void main(String argv []) throws Exception {
            // Input variable.
            final aI iVar = new bI();
            // Output variable.
            aO temp = null;
            // the three lines below must be done in some generic place within the program. 
            ConverterFactory factory = new ConverterFactory();
            new bIConverter(factory);
            new cIConverter(factory);

            temp = factory.convert(iVar);
            System.out.println(temp);
        }
}

0
私有静态方法 aO convert(aI iVar) { 如果 (iVar instanceof bI) 返回新的 bO(); 如果 (iVar instanceof cI) 返回新的 cO(); 否则 返回新的 aO(); }

我正在尝试避免使用 instanceof,因为我有很多类。 - sjcomp
那跟任何事情有什么关系? - Erick Robertson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接