Switch 类 有什么用?

What are Switch classes used for?

开关 类(派生自 org.eclipse.emf.ecore.util.Switch<T>)有什么用?

javadoc 解释为

An abstract base class for all switch classes.

这没有帮助,因为我以前从未听说过 "switch classes"。

Java 中的所有内容都是 class。 (原语除外)。因此,当您使用 switch 时,您会在幕后调用具有 switch 的所有逻辑的 class。 Switch<T> 可能是 class,其他开关 class 继承并在您使用它们时实现方法。例如,您可以使用 switch(int) 或者您可以使用 switch(String),这两种功能是不同的,必须在某处定义。

开关 class 是一个 class,它允许您选择和实例化基于模型对象(在本例中为 EMF 模型对象)的类型的具体实例。我看到的示例表明它对于为 EMF 模型实例化特定于类型的适配器很有用。

您可以通过覆盖 doSwitch 方法来使用它。例如,假设我有一个模型对象,我想实例化一个与模型中的 type 值相对应的适配器对象:

public class ExampleSwitch extends Switch<Adapter> {

    public Adapter doSwitch(EObject eobject) {
        if (eobject instanceof MyModel) {
            switch (eobject.getType()) {
                case TYPEA:
                    Adapter result = createTypeAAdapter(eobject);
                    if (result == null) {
                        return createDefaultAdapter(eobject);
                    }
                    return result;
                case TYPEB:
                    ...
                default:
            }
        }
    } 
}

eclipse org.eclipse.emf.common.notify.AdapterFactory 然后将其用于 return 适配器。

public class MyAdapterFactory implements AdapterFactory {
    public boolean isFactoryForType(Object o) {
        return (o instanceof MyModel);
    }
    public Adapter adapt(Notifier notifier, Object type) {
        return mySwitch.doSwitch((EObject)notifier);
    }
}  

我从 here 中提取了这些信息。我还没有验证这一点,但显然 EMF 生成器可以有选择地为您生成 AdapterFactories 和 Switch classes。

EMF 为您的元模型生成一个实用程序 class 派生自 org.eclipse.emf.ecore.util.Switch<T>,允许您 "easily" 根据 objects 的实际类型访问您的 EMF 模型,采取考虑到继承关系。

确实,生成的实用程序中的 Javadoc class 指出

The Switch for the model's inheritance hierarchy. It supports the call doSwitch(object) to invoke the caseXXX method for each class of the model, starting with the actual class of the object and proceeding up the inheritance hierarchy until a non-null result is returned, which is the result of the switch.

这样,您就不必自己手动实现 Visitor class,您可以依赖此类生成的实用程序 class。生成的开关 class 也是一个通用的 class,因此您可以使用它来计算任何类型的值。

我们的想法是,您为生成的开关 class 创建一个子 class,并仅覆盖要处理的模型元素的 caseXXX 方法。然后你创建一个这样的子class的实例,你调用doSwitch传递你的模型的一个实例。

您的元模型中的每个 XXX 类型都有一个 caseXXX 方法。您还有一个用于默认情况的 defaultCase() 方法,即当没有其他情况方法匹配时。

例如,如果您的元模型中有类型 AB1B2,那么您在生成的开关 class 中有方法 caseAcaseB1caseB2,您可以重新定义。请记住,默认实现只是 returns null。如果 B1B2A 的子类型,并且您只重新定义 caseAcaseB1,那么如果您调用 doSwitch 传递 B1 则将使用 caseB1 方法。如果你调用 doSwitch 传递 B2 那么方法 caseA 将被使用:事实上,没有 caseB2,但是 B1 是 [=14] 的子类型=].

例如,对于 Ecore 模型本身,您有 org.eclipse.emf.ecore.util.EcoreSwitch。这是一个 JUnit 测试形式的示例,其中创建了 EcoreSwitch 的 subclass 来访问 Ecore 模型(它只是 returns 带有一些信息的字符串,请注意它还访问了 children):

public class EcoreSwitchTest {

    private static EcoreFactory factory = EcoreFactory.eINSTANCE;

    @Test public void exampleEcoreSwitch() {
        // given
        EAttribute eAttribute = factory.createEAttribute();
        EReference eReference = factory.createEReference();
        EClass eClass = factory.createEClass();
        eClass.getEStructuralFeatures().add(eAttribute);
        eClass.getEStructuralFeatures().add(eReference);

        EDataType eDataType = factory.createEDataType();

        EPackage ePackage = factory.createEPackage();
        ePackage.getEClassifiers().add(eClass);
        ePackage.getEClassifiers().add(eDataType);

        EcoreSwitch<String> mySwitch = new EcoreSwitch<String>() {
            @Override
            public String caseEClass(EClass c) {
                return "EClass\n" +
                        c.getEStructuralFeatures().stream()
                            .map(this::doSwitch)
                            .collect(Collectors.joining());
            };

            @Override
            public String caseEAttribute(EAttribute a) {
                return "  EAttribute\n";
            };

            @Override
            public String caseEStructuralFeature(EStructuralFeature f) {
                return "  EStructuralFeature("
                        + f.eClass().getName() + ")\n";
            };

            @Override
            public String defaultCase(EObject o) {
                return "Unknown\n";
            };
        };

        // when
        String result = ePackage.getEClassifiers().stream()
                            .map(o -> mySwitch.doSwitch(o))
                            .collect(Collectors.joining());

        // then
        assertEquals(
                "EClass\n" + // caseEClass
                "  EAttribute\n" + // caseEAttribute
                "  EStructuralFeature(EReference)\n" + // caseEStructuralFeature
                "Unknown\n" + // defaultCase
                "", result);
    }
}

如您所见,caseEStructuralFeature 捕获了一个 EReference,因为没有 caseEReference 而不是 EAttribute,因为有更具体的 caseEAttribute

同样,通过从 EMF 为您的元模型生成的开关 class 派生,您可以轻松访问您的模型。