繁体   English   中英

Java:创建具有变量类型的对象并在具有变量名称的对象上调用方法

[英]Java: Creating objects with variable type and calling methods on an object with variable name

我有一个带有 String 类型键和 ArrayList 类型值的哈希图。

它看起来像这样:

key: ".avi", value: {new VideoResource(var), "videoResources"}

我想做以下事情:

1. 创建一个与哈希图中找到的变量类型相同的新变量。 这次我想创建一个新的 VideoResource,但它可以是任何内容(AudioResource、ImageResource...),具体取决于哈希图中的内容。 然而,这些类型总是采用相同的参数“var”。

所以,我想做:

SomeResource resource = new SomeResource(var);

其中 SomeResource 由某个对象的类型决定。

2. 调用先前实例化的对象上的方法,名称为

String objectName = hashMap.get(key).get(1);

该对象将始终具有该方法,并且被调用的方法将始终具有相同的名称。

所以,我想做:

objectName.methodName();

其中 objectName 由某个字符串决定。

我怎样才能做到这一点?

编辑

上下文是这样的:

对于一项任务,我的任务是重构开源项目 FreeCol 中的一些代码。 我正在重构的方法是如下所示的 createResource。 它的问题在于,除了非常重复的 if 语句之外,它还违反了开放/封闭原则。 我们希望它对扩展开放,即添加新的扩展类型(.avi 等),但对修改关闭,即您不应该修改一大堆代码来这样做。

该方法如下所示:

public static void createResource(URI uri, ResourceSink output) {
if(findResource(uri, output))
    return;

try {
    if ("urn".equals(uri.getScheme())) {
        if (uri.getSchemeSpecificPart().startsWith(ColorResource.SCHEME)) {
            ColorResource cr = new ColorResource(uri);
            output.add(cr);
            colorResources.put(uri, new WeakReference<>(cr));
        } else if (uri.getSchemeSpecificPart().startsWith(FontResource.SCHEME)) {
            FontResource fr = new FontResource(uri);
            output.add(fr);
            fontResources.put(uri, new WeakReference<>(fr));
        }
    } else if (uri.getPath().endsWith("\"")
            && uri.getPath().lastIndexOf('"',
                    uri.getPath().length()-1) >= 0) {
        StringResource sr = new StringResource(uri);
        output.add(sr);
        stringResources.put(uri, new WeakReference<>(sr));
    } else if (uri.getPath().endsWith(".faf")) {
        FAFileResource far = new FAFileResource(uri);
        output.add(far);
        fafResources.put(uri, new WeakReference<>(far));
    } else if (uri.getPath().endsWith(".sza")) {
        SZAResource szr = new SZAResource(uri);
        output.add(szr);
        szaResources.put(uri, new WeakReference<>(szr));
    } else if (uri.getPath().endsWith(".ttf")) {
        FontResource fr = new FontResource(uri);
        output.add(fr);
        fontResources.put(uri, new WeakReference<>(fr));
    } else if (uri.getPath().endsWith(".wav")) {
        AudioResource ar = new AudioResource(uri);
        output.add(ar);
        audioResources.put(uri, new WeakReference<>(ar));
    } else if (uri.getPath().endsWith(".ogg")) {
        if (uri.getPath().endsWith(".video.ogg")) {
            VideoResource vr = new VideoResource(uri);
            output.add(vr);
            videoResources.put(uri, new WeakReference<>(vr));
        } else {
            AudioResource ar = new AudioResource(uri);
            output.add(ar);
            audioResources.put(uri, new WeakReference<>(ar));
        }
    } else {
        ImageResource ir = new ImageResource(uri);
        output.add(ir);
        imageResources.put(uri, new WeakReference<>(ir));
    }
    catch (Exception e) {
        logger.log(Level.WARNING, "Failed to create resource with URI: " + uri, e);
    }
}

所以我想要做的是摆脱处理文件扩展名的所有其他 ifs,并用对创建正确资源的方法 assignResource 的单个调用替换它们

SomeResource resource = new SomeResource(uri);

将其添加到输出

output.add(resource);

并将其放入正确的 WeakHashMap

someResource.put(uri, new WeakReference<>(resource));

这些哈希映射声明如下:

private static final Map<URI, WeakReference<ColorResource>> colorResources = new WeakHashMap<>();
private static final Map<URI, WeakReference<FontResource>> fontResources = new WeakHashMap<>();
private static final Map<URI, WeakReference<StringResource>> stringResources = new WeakHashMap<>();
private static final Map<URI, WeakReference<FAFileResource>> fafResources = new WeakHashMap<>();
private static final Map<URI, WeakReference<SZAResource>> szaResources = new WeakHashMap<>();
private static final Map<URI, WeakReference<VideoResource>> videoResources = new WeakHashMap<>();
private static final Map<URI, WeakReference<ImageResource>> imageResources = new WeakHashMap<>();

我有一些代码可以从 URI 中提取文件扩展名,并将其作为键,以及关联的对象(即 VideoResource())和对象的名称,我们要调用 put() 方法(即“ videoResources"),作为值。

这个想法是,如果你要添加一个新的扩展类型,你只需要对我的方法 addResource 做一个函数调用:

addResource(resourceMap, ".ogg", new VideoResource(uri), "videoResources");

将这些参数添加到它可以处理的文件扩展名映射中。

而不是所有 else if 语句,对方法 assignResource 的单个调用

assignResource(uri, resourceMap);

将被制作。

所以我面临的问题是如何创建一个与我的哈希图中找到的类型相匹配的新对象,然后在正确的 WeakHashMap(videoResources 等)上调用 put() 方法。

编辑

更多问题。

该行output.add(resource); 给出错误,因为资源是资源而不是子类型之一。 下一行, resources.add(uri, resource); 抱怨类型安全,引用应该被参数化。 我将界面更改为您建议的第二个通用界面。 一个 Resources 实现现在看起来像这样的例子:

class StringResources implements Resources<StringResource> {

    private final Map<URI, WeakReference<Resource>> resources = new WeakHashMap<>();
    @Override
    public boolean matches(URI uri) {
        return uri.getPath().endsWith("\"")
            && uri.getPath().lastIndexOf('"', uri.getPath().length() - 1) >= 0;
    }

    @Override
    public StringResource from(URI uri) {
        return new StringResource(uri);
    }

    @Override
    public void add(URI uri, StringResource resource) {
        resources.put(uri, new WeakReference<>(resource));
    }  
}

这就是你的意思他们应该看起来吗? 在这种情况下,我们应该如何更改线条

Resource resource = resources.from(uri);
output.add(resource);
resources.add(uri, resource);

这样当我们调用 output.add 时资源是正确的子类型?

我会为您的问题提供答案,但您尝试做的很可能不是解决您遇到的任何问题的最佳解决方案。 提供更多的上下文可能会导致更好的方法。

class VideoResource {

    public VideoResource(Object var) {
    }

    public void videoResources() {
        System.out.println("It was called");
    }
}

class Example {

    public static void main(String[] args) {
        String key = ".avi";

        Map<String, List<Object>> map = prepareExample(key);

        Class<?> unknownClass = map.get(key).get(0).getClass();

        try {
            // getConstructor accepts class of parameter type
            Constructor<?> constructor = unknownClass.getConstructor(Object.class);
            Object newInstance = constructor.newInstance("abc");
            Method method = newInstance.getClass().getMethod((String) map.get(key).get(1));
            method.invoke(newInstance);
        } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static Map<String, List<Object>> prepareExample(String key) {
        Map<String, List<Object>> map = new HashMap<>();
        List<Object> list = new ArrayList<>();
        list.add(new VideoResource(null));
        list.add("videoResources");
        map.put(key, list);
        return map;
    }
}

编辑:

好吧。 根据呈现的上下文,我有一个解决方案的命题。 我将从创建两个接口开始:

interface Resource {
}

interface Resources {
    boolean matches(URI uri);
    Resource from(URI uri);
    void add(URI uri, Resource resource);
}

我相信这些资源必须有一些通用接口,因为 ResourceSink 类接受它们中的任何一个。 我没有创建完整的解决方案,但经过一段时间的解释,您应该能够自己完成。 下面针对此项目中存在的两个资源提供了两个接口的示例实现:

class ColorResource implements Resource {
    ColorResource(URI uri) {
    }
}

class ColorResources implements Resources {

    private final String COLOR_RESOURCE_SCHEMA = "";

    private final Map<URI, WeakReference<Resource>> resources = new WeakHashMap<>();

    @Override
    public boolean matches(URI uri) {
        return uri.getSchemeSpecificPart().startsWith(COLOR_RESOURCE_SCHEMA);
    }

    @Override
    public Resource from(URI uri) {
        return new ColorResource(uri);
    }

    @Override
    public void add(URI uri, Resource resource) {
        resources.put(uri, new WeakReference<>(resource));
    }
}

class StringResource implements Resource {
    StringResource(URI uri) {
    }
}

class StringResources implements Resources {

    private final Map<URI, WeakReference<Resource>> resources = new WeakHashMap<>();
    @Override
    public boolean matches(URI uri) {
        return uri.getPath().endsWith("\"")
            && uri.getPath().lastIndexOf('"', uri.getPath().length() - 1) >= 0;
    }

    @Override
    public Resource from(URI uri) {
        return new StringResource(uri);
    }

    @Override
    public void add(URI uri, Resource resource) {
        resources.put(uri, new WeakReference<>(resource));
    }
}

正如你所看到的,Resources 接口能够判断它是否应该从给定的 uri(匹配)创建资源,创建它(来自)并在内部存储(添加)。 它里面有那些 WeakHashMap。 最后一件事是改变你的主要方法:

class Stack464 {

    private static Logger logger = Logger.getLogger(Stack464.class.toString());

    private static final Resources colorResources = new ColorResources();
    private static final Resources stringResources = new StringResources();

    public static void createResource(URI uri, ResourceSink output) {
        try {
            Stream.of(colorResources,
                      stringResources)
                .filter(resources -> resources.matches(uri))
                .findFirst()
                .ifPresent(resources -> {
                    Resource resource = resources.from(uri);
                    output.add(resource);
                    resources.add(uri, resource);
                });
        } catch(Exception e){
            logger.log(Level.WARNING, "Failed to create resource with URI: " + uri, e);
        }
    }
}

如您所见,我用资源类型的字段替换了每个地图字段。 然后在 main 方法中,我从字段中创建了一个流。 然后我试图在其中找到第一个匹配项 - 我正在寻找能够从给定 uri 创建资源的资源类。 然后我首先使用它们,创建一个资源,将它添加到 Sink 和内部资源映射。

现在要添加新的资源类型,您需要实现两个接口,添加新字段并修改 Stream#of 中的字段。

有两件事我需要提一下:

  1. 在您的代码中,您可以使用存储在地图中的资源,并且它们具有适当的值类型,如 ColorResources 等。在我的解决方案中,内部地图必须具有 WeakReference 值,因为它们产生类资源的对象。 因此,要实际使用它,您必须对其进行转换(但您可以在具体的 Resources 实现中安全地使用它)。
  2. Stream#of 中字段的顺序很重要。 在您的代码中,当 uri 的后缀是“.video.ogg”时创建 VideResource,如果它是“.ogg”则创建 AudioResource。 如果您将 AudioResources 放在 Stream#of 中的 VideoResources 之上,那么即使它是视频资源,代码也会在出现后缀“.ogg”时创建音频资源。 将 VideoResources 放在 AudioResources 之上解决了这个问题。 因此,您必须将 Stream#from 中的字段从更具体到更一般的匹配模式排序。

第一个问题可以避免将接口资源更改为

interface Resources<T extends Resource> {
    boolean matches(URI uri);
    T from(URI uri);
    void add(URI uri, T resource);
}

然后class ColorResources implements Resources<ColorResource>可以具有引用 ColorResource 的内部 WeakHashMap。 这将导致在原始资源类型上调用 Resources#add,就像在 ifPresent 方法中一样,我们不知道哪个实现处理它。

编辑:

要使用 ResourceSink 的重载方法 add,我只有一种解决方案。 这是访问者模式中使用的方法。 首先修改你的Resource接口:

interface Resource {
    void putIn(ResourceSink resourceSink);
}

现在示例实现如下所示:

class ColorResource implements Resource {
    ColorResource(URI uri) {
    }

    @Override
    public void putIn(ResourceSink resourceSink) {
        resourceSink.add(this);
    }
}

在 ColorResource 内部, this具有 ColorResource 类型,因此调用了预期的 ResourceSink#add 方法。 然后你改变 main 方法来利用这个新功能。

Resource resource = resources.from(uri);
resource.putIn(output);
resources.add(uri, resource);

现在关于这个对 Resources 类的不安全添加调用。 如果你真的需要引用哈希映射中的具体类,那么我不知道更好的方法。 如果你不这样做,那么不使用泛型就可以解决问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM