簡體   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