Java:使用变量类型创建对象并在具有变量名

时间:2017-09-29 16:11:07

标签: java

我有一个哈希映射,键的类型为String,值类型为ArrayList。

它看起来像这样:

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

我想做以下事情:

1。 创建一个与hashmap中的变量类型相同的新变量。这次我想创建一个新的VideoResource,但它可以是任何东西(AudioResource,ImageResource ...),具体取决于hashmap中的内容。但是,类型总是采用相同的参数“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);

将成为。

所以我遇到的问题是如何创建一个与我的hashmap中找到的类型相匹配的新对象,然后在右边的WeakHashMap(videoResources等)上调用方法put()。

修改

更多问题。

output.add(resource);给出错误,因为资源是资源而不是其中一个子类型。下一行resources.add(uri, resource);抱怨类型安全,应该参数化参考。我将界面更改为您的第二个建议的通用界面。现在,资源实现的示例如下:

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?

时,资源属于正确的子类型

1 个答案:

答案 0 :(得分:1)

我会为您提供问题的答案,但您尝试做的事情很可能不是您遇到的任何问题的最佳解决方案。提供更多背景可以带来更好的方法。

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值,因为它们 生成类Resource的对象。所以实际使用它你会 必须抛出它(但你可以在具体的资源中安全地进行 实现)。
  2. Stream#中字段的顺序。在你的代码中,当uri的后缀为&#34; .video.ogg&#34;时,你会创建VideResource。和 AudioResource如果是&#34; .ogg&#34;。如果你把AudioResources放在上面 Stream中的VideoResources即使它是一个视频 资源代码将创建音频资源作为后缀&#34; .ogg&#34; 发生。将VideoResources置于AudioResources之上可以解决问题 问题。因此,您必须从更具体的内容中订购Stream#中的字段 更一般的匹配模式。
  3. 第一个问题是可以避免将界面资源更改为

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

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

    修改

    要使用ResourceSink的重载方法添加,我只有一个解决方案。这是访客模式中使用的方法。首先修改您的资源界面:

    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方法。然后你改变主要方法来利用这个新功能。

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

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