[英]Why aren't classes first class objects in Java?
有人可以向我解釋為什么類不是第一個 class 對象在 Java 中嗎? 如果確實如此,某些模式會非常有效,我一直在用 getter 和 setter 編寫工廠類,只是向類中添加不必要的垃圾,這樣我就可以將實例傳遞給方法而不是實際的類來分解泛型代碼位。 例如:
public class AsyncBookSearch extends AsyncTask<String,BookItem,Void> {
public ListView list;
public AmazonItemAdapter<BookItem> adapter;
public AsyncBookSearch(ListView l,AmazonItemAdapter<BookItem> ad) {
list = l;
adapter = ad;
}
@Override
protected Void doInBackground(String... keywords) {
// TODO Auto-generated method stub
new BookSearch(keywords[0],list,adapter).parse();
return null;
}
}
我有幾個這樣的類,如果我想讓整個東西通用,那么doInBackground()
中的東西將導致幾個額外的方法和參數傳遞中的其他類型的重復,如果我可以編寫以下內容就不會成為問題:
public class AsyncItemSearch<T extends GenericItemSearch<S>,S extends GenericItem> extends AsyncTask<String,T,Void> {
public ListView list;
public AmazonItemAdapter<S> adapter;
public AsyncBookSearch(ListView l,AmazonItemAdapter<S> ad) {
list = l;
adapter = ad;
}
@Override
protected Void doInBackground(String... keywords) {
// TODO Auto-generated method stub
new T(keywords[0],list,adapter).parse();
return null;
}
}
目前我不能在 Java 中編寫這樣的代碼。我必須在幾乎所有涉及的 class 中引入不必要的耦合,這只會讓事情變得更加復雜,因為現在每個實例不僅需要擔心它自己的 state,而且還需要擔心 state與它的任務完全無關的對象。
我不認為你的問題實際上是關於類是“第一類”對象。 我懷疑它與java如何處理泛型有關。 我認為你需要理解類型擦除:在你的例子中,你基本上想要寫類似的東西
void foo<T>{
T bar = new T();
}
但這是不可能的,因為在運行時,由於類型擦除 ,沒有關於T類實際是什么的信息:
當實例化泛型類型時,編譯器通過稱為類型擦除的技術來轉換這些類型 - 這是一種編譯器刪除與類型參數相關的所有信息並在類或方法中鍵入參數的過程。 類型擦除使得使用泛型的Java應用程序能夠維護與泛型之前創建的Java庫和應用程序的二進制兼容性。
類是Java中的第一類對象(類Class)。 你可以將它們分配給變量,傳遞,從函數返回等等。我想你想問一些其他的問題,但我不確定那個問題是什么。 也許關於擦除與泛型類型的具體化有關? 答案可能很有趣。
你繼續寫作的一個例子可能會有所幫助。
另外,考慮一下這個問題之間的區別,比如'X有理由'和'X有好用嗎'。
編輯:(回答評論:“我還沒有看到一個類傳遞的例子”(順便說一句:我仍然認為這是關於擦除與具體化的問題,但這個具體的評論沒有解決它)。我很驚訝你有在Java中傳遞類是非常常見的。很少有真正流行的API的例子在沒有思考的情況下浮現在我腦海中:
Hibernate / JPA實體管理器根據其類及其主鍵查找映射對象; 例如: Invoice i = entityManager.find(Invoice.class, 12l)
GWT使用一個特殊的工廠來注入僅存在於生成的javascript中的類(或以其他方式參數化); 該方法采用類的實例來創建; 例如: Resource res1 = GWT.create(MyResources.class);
Spring的ApplicationContext根據您傳遞給getBean方法的類為您提供bean; 所以你會這樣做: DataSource default = applicationContext.getBean(DataSource.class);
在C#使用反射的情況下使用類實例(而Java不能,因為它在運行時擦除泛型類型); 該模式有時被稱為“類令牌”
在上面的大多數情況下,您將看到類文字(作為第一類對象類在Java中具有文字)而不是動態調用,但這主要是因為語言的靜態特性(以及使用它的程序員) 。 在編譯時知道類型通常被認為是一件好事。
由於Java在運行時不支持泛型,因此無法即時創建泛型類。 這沒有什么區別,因為您可以創建一個非泛型類並將其用作通用類。
回答其他注釋:通常使用在運行時創建和修改類,但主要由Java基礎結構使用:應用程序服務器,庫。 看看JMockit。 您可以實際修改現有類或在方法調用期間動態替換其方法。
課程是一流的。 它們是Class類的對象。 它們可以分配給變量和字段,作為參數傳遞,甚至可以動態創建。
您遇到的問題是泛型類型沒有具體化,它們只在編譯時可見。 此外,因為構造函數不是繼承的,除非您確切地知道要創建哪個類,否則您無法知道是否存在具有所需參數的構造函數。 解決您遇到的問題的一種方法是傳入工廠類。
public class AsyncItemSearch<T extends GenericItemSearch<S>,S extends GenericItem> extends AsyncTask<String,T,Void> {
public ListView list;
public AmazonItemAdapter<S> adapter;
public GenericItemSearchFactory<T> factory;
public AsyncBookSearch(ListView l,AmazonItemAdapter<S> ad, GenericItemSearchFactory<T> factory) {
list = l;
adapter = ad;
this.factory = factory;
}
@Override
protected Void doInBackground(String... keywords) {
this.factory.getInstance(keywords[0],list,adapter).parse();
return null;
}
}
我不認為類是第一類對象,而是我會說Class
是反射API的一部分,因為你不能像其他更動態的語言一樣自由地使用它。 也就是說,如果沒有反射,就無法創建新實例。
主要原因是java的元模型。 您不能覆蓋靜態方法,並且類中存在的構造函數不一定存在於其子類中。 這也是你的代碼new T(keywords[0],list,adapter)
不起作用的原因,子類可能沒有這樣的構造函數。
因此,在java中,沒有用於類對象,因為您肯定需要反射來檢查代碼是否在運行時有效。
不同的主題是泛型類型參數。 你不能做T.class
因為genrics在某種程度上是java中的一種語言攻擊(沒有什么比C ++模板更好)。 其主要原因是與舊版Java兼容。
但是,您可以使用上面提到的反射API解決此問題:
public Foo<T extends Bar> {
private Class<T> barClass;
public Foo(Class<T> barClass) {
this.barClass = barClass;
}
public T createSomeBar(String arg) {
try {
// the bar contract says that subclasses must have such a constructor
return barClass.getConstructor(String.class).newInstance(arg);
} catch ... // the contract was violated, do proper handling
}
}
課程是一流的項目。 您可以在類上調用“newinstance”方法來創建實例,也可以請求構造函數對象並使用它。 試試這段代碼:
public class AsyncItemSearch<T extends GenericItemSearch<S>,S extends GenericItem> extends AsyncTask<String,T,Void> { private Constructor<T> searchConstructor; public ListView list; public AmazonItemAdapter<S> adapter; public AsyncBookSearch(Class<T> theClass, ListView l,AmazonItemAdapter<S> ad) { list = l; adapter = ad; searchConstructor = theClass.getConstructor(String.class, ListView.class, AmazonItemAdapter<S>.class); } @Override protected Void doInBackground(String... keywords) { // TODO Auto-generated method stub searchConstructor.newInstance(keywords[0],list,adapter).parse(); return null; } }
這樣稱呼它:
AmazonItemAdapter<Book> amazonAdapter = new AmazonBookAdapter(); AsyncItemSearch<BookSearch,Book> s = new AsyncItemSearch<BookSearch,Book>( BookSearch.class, myListView, amazonAdapter ); s.doInBackground("have", "at", "thee", "!");
我剛剛注意到 Java 中對第一個 class generics 的引用。Guy L. Steele 和我在 1998 年的 OOPSLA 會議上寫了一篇論文,提出了一種方法來為 Java 添加對一流 generics 的支持,遺憾的是沒有被 Sun Microsystems 委員會采納控制 Java 的進化。參見https://dl.acm.org/doi/abs/10.1145/286936.286958 。 我和我的研究生隨后表明,這種方法在效率上與擦除相當(使用 Java JIT 編譯器技術,該技術未調整為優化我們在 Java 泛型實現中使用的習語)。 見https://www.semanticscholar.org/paper/Efficient-Implementation-of-Run-time-Generic-Types-Allen-Cartwright/cb07594f5e070e54ff891d064430c41f2091e2b4和https://www.csjarice.edu/ sac2006.pdf 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.