2014-09-29 91 views
13

比方說,我需要爲界,通用Comparable類型的類:包封物仿製藥

class A<T extends Comparable<T>> { 

    // this is just an example of usage of T type 
    List<T> comparables; 

    int compareSomething(T smth) { 
     return comparables.get(0).compareTo(smth); 
    } 
} 

類有在簽名自己的泛型方法:

<V> Future<V> submit(Callable<V> task) { 
    return someExecutorService.submit(task); 
} 

現在,有限制submit方法的輸入只接受Callables也實現T的可能性?我第一次嘗試這樣的:

<V, W extends T & Callable<V>> Future<V> submit(W task) { 
    if(compareSomething(task) != 0) 
     throw new RuntimeException(""); 
    return someExecutorService.submit(task); 
} 

,但發現這是不可能的(原因描述here)。有沒有優雅的可能性繞過它?


編輯:一個醜陋可能性我能想到的是在兩種不同類型的分裂封裝,並通過在submit對象對:

class A<T extends Comparable<T>> { 

    // this is just an example of usage of T type 
    List<T> comparables; 

    int compareSomething(T smth) { 
     return comparables.get(0).compareTo(smth); 
    } 
    <V> Future<V> submit(Callable<V> task, T comparable) { 
     if(compareSomething(comparable) != 0) 
      throw new RuntimeException(""); 
     return someExecutorService.submit(task); 
    } 
} 

的主要缺點是,該方法簽名變得比較複雜,我也需要一段後者代碼有些人Callable■一個一對一的映射T秒。 ?也許我們可以認爲,解決它以適當的方式..


編輯模式,採取兩種:讓我來簡單解釋一下我想要實現的。我正在開發一個能夠執行某種特殊任務調度的自定義線程池實現。爲此,該服務只接受一種特殊的Callable任務。這些Callable■找實現自定義界面,類似於Comparable之一。通過比較使用此界面中的方法的任務對,該服務將:

  1. 如果傳入任務被任何正在運行的任務阻塞,則阻止該傳入任務。
  2. 在完成任務的Future完成調用尚未完成的任務。
  3. 通過比較他們確定的待處理任務的執行順序。

阻擋/比較邏輯應該由任務本身來提供。這樣,線程池類應該只定義池對象正在接受什麼樣的特殊類型,它根本不關心它們是什麼類型的,它們的返回類型是什麼。


編輯,採取三種:基於Erick Robertsonanswer,它現在能夠防止發臭任務提交:

public static void test(String[] args) { 
    A<Valid> scheduler = new A<>(); 
    scheduler.betterSubmit(new Valid()); // applies to method signature 
    scheduler.betterSubmit(new Forbidden()); // rejected on compile time 
    scheduler.betterSubmit(new ConformWithValid()); // still appliable because all required interfaces implementations recognised 
} 

// just a bunch of test classes 

private static class Valid implements Comparable<Valid>, Callable<Void> { 

    @Override 
    public int compareTo(Valid o) { 
     return 0; 
    } 

    @Override 
    public Void call() throws Exception { 
     return null; 
    } 
} 

private static class Forbidden implements Comparable<Forbidden>, Callable<Void> { 

    @Override 
    public int compareTo(Forbidden o) { 
     return -1; 
    } 

    @Override 
    public Void call() throws Exception { 
     return null; 
    } 
} 

private static class ConformWithValid implements Comparable<Valid>, Callable<Boolean> { 

    @Override 
    public int compareTo(Valid o) { 
     return 1; 
    } 

    @Override 
    public Boolean call() throws Exception { 
     return Boolean.FALSE; 
    } 
} 

尼斯和容易!希望有一天這能幫助和我一樣的人。 :-)

+4

密切相關:?在多重限制的類型參數爲什麼我不能用一個類型參數(http://stackoverflow.com/q/197190 ) – 2014-09-29 09:44:43

回答

3

使用Comparable<T>限制參數,而不是僅限於T

如果唯一標準是對象是CallableComparable,那麼您可以將這兩個接口放在參數上。如果需要填寫另一個需求,甚至可以選擇向參數添加一個命名類。你需要禁止一個警告,但這是一個安全的壓制,因爲你知道T延伸Comparable<T>

public class A<T extends Comparable<T>> { 

    // this is just an example of usage of T type 
    List<T> comparables; 

    ExecutorService someExecutorService = null; 

    int compareSomething(T smth) { 
     return this.comparables.get(0).compareTo(smth); 
    } 

    <V> Future<V> submit(Callable<V> task) { 
     return this.someExecutorService.submit(task); 
    } 

    @SuppressWarnings("unchecked") 
    <V, W extends Callable<V> & Comparable<T>> Future<V> betterSubmit(W task) { 
     if(this.compareSomething((T) task) != 0) 
      throw new RuntimeException(""); 
     return this.someExecutorService.submit(task); 
    } 
} 

我碰到的另一個類,我有它本身作爲參考這樣自己的通用參數同樣的問題。我不認爲這是一個乾淨的實施,但我喜歡你的榜樣。它提供了一個很好的整潔用例,我可能會在後面設計一些新的代碼。我通常不喜歡壓制警告,但我並不介意,因爲只有這種方法受到影響。我希望這個能幫上忙!

0

這是否解決問題?

class A<V, T extends MyT<T, V>> { 

    // this is just an example of usage of T type 
    List<T> comparables; 

    int compareSomething(T smth) { 
     return comparables.get(0).compareTo(smth); 
    } 

    public <V> Future<V> submit(MyT<T, V> task) { 
     return ...; 
    } 
} 

class MyT<T, V> implements Comparable<T>, Callable<V> { 

    @Override 
    public int compareTo(T o) { 
     return 0; 
    } 

    @Override 
    public V call() 
     throws Exception { 

     return ...; 
    } 
} 
+0

不幸的是,因爲MyT 不符合'T','compareSomething'不適用於'MyT'。 (你也可以想象將提交的任務添加到「comparables」列表或任何類似的東西,而不是調用'compareSomething',那麼確切的功能在這一點上並不重要。)另外 - 雖然這不會影響實際問題 - t想要將整個類綁定到特定的返回類型'Callable's,所以我不能在類簽名中允許'V'。 – hoefling 2014-09-30 15:57:44

0

像這樣的東西可以工作:

class A<T extends Comparable<T>> { 
    List<T> comparables; 

    int compareSomething(T smth) { 
     return comparables.get(0).compareTo(smth); 
    } 

    public Future<T> submit(B<T> task) { 
     return someExecutorService.submit(task); 
    } 

    public class B<T> implements Callable<T> { 
     ... 
    } 
} 
+0

你錯過了點 - 請再次看看我的'submit'方法... – hoefling 2014-10-01 16:37:16

0

OK,真是最後一次嘗試。 你可以改變你的泛型函數爲泛型嵌套或內部類嗎?就像這樣:

class A<T extends Comparable<T> > { 


    // this is just an example of usage of T type 
    List<T> comparables; 

    int compareSomething(T smth) { 
     return comparables.get(0).compareTo(smth); 
    } 

    class B<V> 
    { 
     T t; 
     T getT() { return t; } 
     Callable <V> v; 
     Callable <V> getV() { return v; } 
     public Future<V> submit(B<V> task) { 
      if(compareSomething(task.getT()) != 0) 
      throw new RuntimeException(""); 
     return SomeExecutorService.submit(task.getV()); 
    } 
} 
+0

同樣的問題在這裏 - 定義'V'作爲'Callable's結果類型將綁定整個類到一個特定類型' Callable's。此外,定義'V extends Callable '根本沒有意義 - 這個類只能繼續'Callable's,返回另一個'Callable's。我會用更多的細節來提高這個問題,以清除那裏的一些霧。 – hoefling 2014-10-02 21:52:49

+0

好的,gotcha。那麼結合這樣的方法怎麼樣?我猜不是--V在單個T內仍然不能自由變化。 – 2014-10-02 22:10:10

+0

仍然存在類受限於「V」的問題。請閱讀我對問題所做的第二次編輯和解釋;希望它能讓整件事更容易理解。儘管如此,我開始認爲通過在全班範圍內改變泛型的使用方式,沒有辦法解決實際問題;我可能需要的是類設計的一個好主意。 – hoefling 2014-10-02 22:17:46

1

我不明白你怎麼可以結合兩者的通用類參數T並在Callable<V>方法參數的V,在答案的鏈接後列出的原因。然而,也許這將是一個選項 - 但我不能估計,雖然它將會改變將任務提交給在任務本身上調用的方法的行爲。沿着線的東西...

class A<T extends Comparable<T> & Callable<?>> { 

    public static abstract class Submittable<T extends Submittable<T,V>,V> 
     implements Comparable<T>, Callable<V> { 
     // ugly, but unavoidable 
     @SuppressWarnings("unchecked") 
     public Future<V> submitTo(A<? super T> scheduler) { 
      return (Future<V>) scheduler.submit((T) this); 
     } 
    } 

    // this is just an example of usage of T type 
    List<T> comparables; 

    int compareSomething(T smth) { 
     return comparables.get(0).compareTo(smth); 
    } 

    // *can* be left public, but that way you enforce a single way 
    // of submitting tasks, namely via submitTo 
    private Future<?> submit(T task) { 
     if(compareSomething(task) != 0) 
      throw new RuntimeException(""); 
     // the following cast is a save conversion - you could also write it cast-free 
     // as Callable<?> callable = task; ...submit(callable); 
     return someExecutorService.submit((Callable<?>) task); 
    } 
} 

現在,你的任務都必須繼承A.Submittable,然後可以通過submitTo提交給調度情況下,返回Future<V>正確類型V

當然,這至關重要依賴於T實際上是Submittable的子類的自我類型。 submitTo發生了什麼 - 很好,很醜陋。但它確實爲您提供了返回正確類型的便利。

+0

謝謝,這可能也是一種選擇,雖然我接受的答案在整個班級中提供的表達較少。 – hoefling 2014-10-07 12:11:55

0

爲什麼不能像這樣簡單的工作?

public class CustomThreadPool { 
    public <CallableReturn> Future<CallableReturn> submit(SelfSchedulingCallable<CallableReturn> task) { 
     // Use comparePriority or any other interface methods needed to determine scheduling 
    } 
} 

public interface SelfSchedulingCallable<V> extends Callable<V> { 
    public boolean isBlockedBy(SelfSchedulingCallable<?> otherTask); // Doesn't care about the return type of otherTask 
    public boolean invokeAfter(SelfSchedulingCallable<?> otherTask); 
    public int comparePriority(SelfSchedulingCallable<?> otherTask); 
} 
2

我認爲埃裏克·羅伯遜的answer最接近的直接解決方案。我想提出一個不太直接的建議(避免任何未經檢查的強制轉換)。您寫道:

此服務只接受一種特殊的Callable任務。這些Callable■找實現自定義界面,類似於Comparable之一。

如果我們實際上宣告你所描述的類型,我們想出了這個路口​​接口:

interface CustomCallable<V> extends Callable<V>, Comparable<CustomCallable<?>> { } 

注意它是如何實現Comparable<CustomCallable<?>>,不Comparable<CustomCallable<V>>。這是因爲我假設CustomCallable s的不同輸出類型仍應該可以在池中相互比較。否則,將V作爲方法類型參數沒有意義。

假設你的游泳池可以接受任何類型的那個路口的接口,我們可以只取出T完全和語言的限制是沒有實際意義:

class A { 

    List<CustomCallable<?>> customCallables; 

    int compareSomething(CustomCallable<?> smth) { 
     return customCallables.get(0).compareTo(smth); 
    } 

    <V> Future<V> submit(CustomCallable<V> task) { 
     if (compareSomething(task) != 0) { 
      throw new RuntimeException(""); 
     } 
     return someExecutorService.submit(task); 
    } 
} 

如果你堅持在一個特定類型的自定義調用的參數化A,它變得更加複雜。使它工作的唯一方法是一種「自我型」參數添加到路口接口:

interface CustomCallable<V, SELF> extends Callable<V>, Comparable<CustomCallable<?, ?>> { } 

這裏,SELF意在代表實施者本身的類型。請注意,然而,無法執行該操作。對自我的類型以及它們對我的答案在這裏告誡更多信息:Is there a way to refer to the current type with a type variable?

隨着加入自主型,它現在可能A申報Tsubmit則堅稱用於自我類型的自定義的可調用它提供:

class A<T extends CustomCallable<?, ?>> { 

    List<CustomCallable<?, T>> customCallables; 

    int compareSomething(CustomCallable<?, T> smth) { 
     return customCallables.get(0).compareTo(smth); 
    } 

    <V> Future<V> submit(CustomCallable<V, T> task) { 
     if (compareSomething(task) != 0) { 
      throw new RuntimeException(""); 
     } 
     return someExecutorService.submit(task); 
    } 
} 

這裏有一個CustomCallable實現,它解決了自身類型的例子:

class MyCustomCallable<V> implements CustomCallable<V, MyCustomCallable<?>> { 
    ... 
} 

類似於之前,請注意浩由於不同的輸出類型可以通過設計互換,所以自我類型被「放寬」到MyCustomCallable<?>而不是MyCustomCallable<V>

下面是一個使用示例:

A<MyCustomCallable<?>> pool = new A<>(); 

MyCustomCallable<String> strCallable = ...; 
MyCustomCallable<Integer> intCallable = ...; 

Future<String> strFuture = pool.submit(strCallable); 
Future<Integer> intFuture = pool.submit(intCallable); 
+0

這也是一個非常好的答案,我喜歡你的想法和乾淨的解釋。不幸的是,我必須將調度程序類綁定到特定的任務類型。儘管如此,你只是給了我一個答案,我現在仍然堅持着另一個問題,所以這是一個恥辱,我不能給你一個賞金...... – hoefling 2014-10-07 12:18:34