简体   繁体   English

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

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

I have a hashmap with keys of type String and values of type ArrayList.我有一个带有 String 类型键和 ArrayList 类型值的哈希图。

It looks something like this:它看起来像这样:

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

I want to do the following things:我想做以下事情:

1. Create a new variable with the same type as that of the one found in the hashmap. 1. 创建一个与哈希图中找到的变量类型相同的新变量。 This time I would want to create a new VideoResource, but it could be anything (AudioResource, ImageResource...) depending on what's inside the hashmap.这次我想创建一个新的 VideoResource,但它可以是任何内容(AudioResource、ImageResource...),具体取决于哈希图中的内容。 The types always take the same parameter "var", however.然而,这些类型总是采用相同的参数“var”。

So, I want to do:所以,我想做:

SomeResource resource = new SomeResource(var);

where SomeResource is decided by the type of some object.其中 SomeResource 由某个对象的类型决定。

2. Call a method on an object, that is previously instantiated, with the name given by 2. 调用先前实例化的对象上的方法,名称为

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

The object will always have the method and the method being called will always have the same name.该对象将始终具有该方法,并且被调用的方法将始终具有相同的名称。

So, I want to do:所以,我想做:

objectName.methodName();

where objectName is decided by some string.其中 objectName 由某个字符串决定。

How can I achieve this?我怎样才能做到这一点?

EDIT编辑

The context is this:上下文是这样的:

For an assignment I've been tasked to refactor a bit of code in the open-source project FreeCol.对于一项任务,我的任务是重构开源项目 FreeCol 中的一些代码。 The method I am refactoring is createResource shown below.我正在重构的方法是如下所示的 createResource。 The problem with it is, that in addition to the very repetitive if-statements, it violates the open/closed principle.它的问题在于,除了非常重复的 if 语句之外,它还违反了开放/封闭原则。 We want it to be open for extension, ie adding a new extension type (.avi etc.), but closed for modification, ie you shouldn't have to modify a whole bunch of code to do so.我们希望它对扩展开放,即添加新的扩展类型(.avi 等),但对修改关闭,即您不应该修改一大堆代码来这样做。

The method looks like this:该方法如下所示:

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

So what I want to do, is to get rid of all the else ifs that handle the file extensions, and replace them with a single call to a method assignResource that creates the right resource所以我想要做的是摆脱处理文件扩展名的所有其他 ifs,并用对创建正确资源的方法 assignResource 的单个调用替换它们

SomeResource resource = new SomeResource(uri);

adds it to the output将其添加到输出

output.add(resource);

and puts it in the right WeakHashMap并将其放入正确的 WeakHashMap

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

These hashmaps are declared as following:这些哈希映射声明如下:

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

I have code that extracts the file extension from the URI, and puts it as a key, along with the associated object (ie VideoResource()) and the name of the object upon with we want to invoke the put() method (ie "videoResources"), as values.我有一些代码可以从 URI 中提取文件扩展名,并将其作为键,以及关联的对象(即 VideoResource())和对象的名称,我们要调用 put() 方法(即“ videoResources"),作为值。

The idea is that if you would add a new extension type, you just have to do one function call to my method addResource:这个想法是,如果你要添加一个新的扩展类型,你只需要对我的方法 addResource 做一个函数调用:

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

that adds these parameters to the map of file extensions it can handle.将这些参数添加到它可以处理的文件扩展名映射中。

Instead of all the else if statements, a single call to a method assignResource而不是所有 else if 语句,对方法 assignResource 的单个调用

assignResource(uri, resourceMap);

would be made.将被制作。

So the problem I faced was how to create a new object of a type that matches that of the one found in my hashmap, and then invoking method put() on the right WeakHashMap (videoResources etc.).所以我面临的问题是如何创建一个与我的哈希图中找到的类型相匹配的新对象,然后在正确的 WeakHashMap(videoResources 等)上调用 put() 方法。

EDIT编辑

More questions.更多问题。

The line output.add(resource);该行output.add(resource); gives an error because resource is a Resource and not one of the subtypes.给出错误,因为资源是资源而不是子类型之一。 The next line, resources.add(uri, resource);下一行, resources.add(uri, resource); complains about type safety, that references should be parameterized.抱怨类型安全,引用应该被参数化。 I changed the interface to your second suggested generic one.我将界面更改为您建议的第二个通用界面。 An example of what a Resources implementation now looks like is this:一个 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));
    }  
}

Is this how you meant they should look?这就是你的意思他们应该看起来吗? In that case, how should we change the lines在这种情况下,我们应该如何更改线条

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

so that the resource is of the right subtype when we call output.add?这样当我们调用 output.add 时资源是正确的子类型?

I'll give you the answer for your question but what you're trying to do is most likely not the best solution for whatever problem you have.我会为您的问题提供答案,但您尝试做的很可能不是解决您遇到的任何问题的最佳解决方案。 Providing more context could result in better approach.提供更多的上下文可能会导致更好的方法。

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

Edit:编辑:

Alright.好吧。 According to presented context I have a proposition of solution.根据呈现的上下文,我有一个解决方案的命题。 I would start with creating two interfaces:我将从创建两个接口开始:

interface Resource {
}

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

I believe there must be some common interface for those resources as ResourceSink class accepts any of them.我相信这些资源必须有一些通用接口,因为 ResourceSink 类接受它们中的任何一个。 I didn't create a full solution but you should be able to do it yourself after a while of explanation.我没有创建完整的解决方案,但经过一段时间的解释,您应该能够自己完成。 Example implementations of both interfaces are presented below for two resources existing in this project:下面针对此项目中存在的两个资源提供了两个接口的示例实现:

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

As you can see Resources interfaces is able to tell if it should create resource from given uri (matches), create it (from) and store internally (add).正如你所看到的,Resources 接口能够判断它是否应该从给定的 uri(匹配)创建资源,创建它(来自)并在内部存储(添加)。 It has those WeakHashMap inside.它里面有那些 WeakHashMap。 Last thing is changing your main method:最后一件事是改变你的主要方法:

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

As you can see I replaced every map field with field of type Resources.如您所见,我用资源类型的字段替换了每个地图字段。 Then in the main method I create a stream from the fields.然后在 main 方法中,我从字段中创建了一个流。 Then I'm trying to find first match among them - I'm looking for resources class able to create a resource from given uri.然后我试图在其中找到第一个匹配项 - 我正在寻找能够从给定 uri 创建资源的资源类。 Then I take first of them, creating a resource, adding it to Sink and to internal resources map.然后我首先使用它们,创建一个资源,将它添加到 Sink 和内部资源映射。

Now to add new resource type you need to implement two interfaces, add new field and modify fields inside Stream#of.现在要添加新的资源类型,您需要实现两个接口,添加新字段并修改 Stream#of 中的字段。

There are two things I need to to mention:有两件事我需要提一下:

  1. in your code you could use the resources stored in map and they had proper value type like ColorResources etc. In My solutions internal maps must have WeakReference value because they produce object of class Resource.在您的代码中,您可以使用存储在地图中的资源,并且它们具有适当的值类型,如 ColorResources 等。在我的解决方案中,内部地图必须具有 WeakReference 值,因为它们产生类资源的对象。 So to actually use it you would have to cast it (but you can safely do it inside concrete Resources implementation).因此,要实际使用它,您必须对其进行转换(但您可以在具体的 Resources 实现中安全地使用它)。
  2. The order of fields in Stream#of matters. Stream#of 中字段的顺序很重要。 In your code you create VideResource when the suffix of uri is ".video.ogg" and AudioResource if it is ".ogg".在您的代码中,当 uri 的后缀是“.video.ogg”时创建 VideResource,如果它是“.ogg”则创建 AudioResource。 If you would put AudioResources above VideoResources in Stream#of then even if it would be a video resource the code would create audio resource as suffix ".ogg" occured.如果您将 AudioResources 放在 Stream#of 中的 VideoResources 之上,那么即使它是视频资源,代码也会在出现后缀“.ogg”时创建音频资源。 Placing VideoResources above AudioResources solves the problem.将 VideoResources 放在 AudioResources 之上解决了这个问题。 So you must order fields in Stream#from from more concrete to more general matching patterns.因此,您必须将 Stream#from 中的字段从更具体到更一般的匹配模式排序。

The first problem could be avoided changing interface Resources to第一个问题可以避免将接口资源更改为

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

Then class ColorResources implements Resources<ColorResource> could have inner WeakHashMap with references to ColorResource.然后class ColorResources implements Resources<ColorResource>可以具有引用 ColorResource 的内部 WeakHashMap。 It would result in calling Resources#add on raw Resources type as in ifPresent method we don't know which implementation handles it.这将导致在原始资源类型上调用 Resources#add,就像在 ifPresent 方法中一样,我们不知道哪个实现处理它。

Edit:编辑:

To use overloaded method add of ResourceSink there is only one solution I have on my mind.要使用 ResourceSink 的重载方法 add,我只有一种解决方案。 This is method used in visitor pattern.这是访问者模式中使用的方法。 First of all modify your Resource interface:首先修改你的Resource接口:

interface Resource {
    void putIn(ResourceSink resourceSink);
}

And now example implementation looks like:现在示例实现如下所示:

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

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

Inside the ColorResource this has ColorResource type so expected ResourceSink#add method is invoked.在 ColorResource 内部, this具有 ColorResource 类型,因此调用了预期的 ResourceSink#add 方法。 Then u change main method to utilize this new function.然后你改变 main 方法来利用这个新功能。

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

Now about this unsafe add call on Resources class.现在关于这个对 Resources 类的不安全添加调用。 If you really need References to concrete classes in hash map then I don't know better approach.如果你真的需要引用哈希映射中的具体类,那么我不知道更好的方法。 If u don't then not using generics solves the problem.如果你不这样做,那么不使用泛型就可以解决问题。

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

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