„Java“ sukurti bendrinio tipo egzempliorių?

Ar galima sukurti generinio tipo instanciją „Java“? Manau, remdamasis tuo, ką mačiau, atsakymas yra no (dėl tipo ištrynimo), bet man būtų įdomu, jei kažkas matytų tai, ką nematau:

 class SomeContainer<E> { E createContents() { return what??? } } 

EDIT: Pasirodo, kad „ Super Type Tokens“ gali būti naudojamas mano problemai išspręsti, tačiau tam reikia daug atspindžių kodo, kaip rodo kai kurie žemiau pateikti atsakymai.

Aš paliksiu jį atidarytą, kad pamatytumėme, ar kas nors ateina su kažkuo, kuris iš esmės skiriasi nuo Ian Robertson Artima straipsnio .

519
16 сент. nustatė Davidas Citronas rugsėjo 16 d 2008-09-16 21:04 '08 at 9:04 pm 2008-09-16 21:04
@ 25 atsakymai

Jūs teisus. Jūs negalite vykdyti new E() . Bet jūs galite jį pakeisti

 private static class SomeContainer<E> { E createContents(Class<E> clazz) { return clazz.newInstance(); } } 

Tai skausmas. Bet tai veikia. Apvyniojimas į gamyklos modelį daro jį nešiojamu.

301
16 сент. Atsakymą pateikė Justin Rudd 16 sep. 2008-09-16 21:10 '08 at 9:10 pm 2008-09-16 21:10

Aš nežinau, ar tai padeda, bet kai jūs subklasuojate (įskaitant anonimiškai) bendrinį tipą, informacija apie tipą pateikiama per refleksiją. Pavyzdžiui.

 public abstract class Foo<E> { public E instance; public Foo() throws Exception { instance = ((Class)((ParameterizedType)this.getClass(). getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(); ... } } 

Taigi, kai esate „Foo“ poklasis, galite gauti „Bar“ kopiją, pvz.,

 // notice that this in anonymous subclass of Foo assert( new Foo<Bar>() {}.instance instanceof Bar ); 

Bet tai veikia daug ir veikia tik poklasiams. Tai gali būti patogu.

121
16 сент. atsakymas duotas Nr 2008-09-16 21:18 '08 9:18 val. 2008-09-16 21:18

Norint perkelti dolerį, jums reikės tam tikros rūšies abstrakčios vienos rūšies gamyklos:

 interface Factory<E> { E create(); } class SomeContainer<E> { private final Factory<E> factory; SomeContainer(Factory<E> factory) { this.factory = factory; } E createContents() { return factory.create(); } } 
86
16 сент. Atsakyti Tom Hawtin - tackline Sep 16 2008-09-16 21:36 '08 at 9:36 pm 2008-09-16 21:36

„Java 8“ galite naudoti Supplier funkcinę sąsają, kad tai būtų galima lengvai pasiekti:

 class SomeContainer<E> { private Supplier<E> supplier; SomeContainer(Supplier<E> supplier) { this.supplier = supplier; } E createContents() { return supplier.get(); } } 

Šią klasę statysite taip:

 SomeContainer<String> stringContainer = new SomeContainer<>(String::new); 

Sintaksė String::new šioje eilutėje yra nuoroda į konstruktorių .

Jei jūsų konstruktorius priima argumentus, galite naudoti lambda išraišką:

 SomeContainer<BigInteger> bigIntegerContainer = new SomeContainer<>(() -> new BigInteger(1)); 
84
30 марта '16 в 19:51 2016-03-30 19:51 Atsakymą pateikė Daniel Prydenas kovo 16 d. 16:51 2016-03-30 19:51
 package org.foo.com; import java.> 
23
03 сент. Atsakymas suteiktas Lars Bohl 03 sep . 2010-09-03 15:41 '10, 15:41, 2010-09-03 15:41

Jei jums reikia naujo tipo argumento, esančio bendrojoje klasėje, pavyzdys, tuomet jūsų konstruktoriai reikalauja savo klasės ...

 public final class Foo<T> { private Class<T> typeArgumentClass; public Foo(Class<T> typeArgumentClass) { this.typeArgumentClass = typeArgumentClass; } public void doSomethingThatRequiresNewT() throws Exception { T myNewT = typeArgumentClass.newInstance(); ... } } 

Naudoti:

 Foo<Bar> barFoo = new Foo<Bar>(Bar.class); Foo<Etc> etcFoo = new Foo<Etc>(Etc.class); 

Argumentai "už":

  • Daug paprastesnis (ir mažiau problemiškas) nei Robertsono „Super Type Token“ (STT) metodas.
  • Daug efektyviau nei STT metodas (kuris turės mobilųjį telefoną pusryčiams).

Suvart:

  • Negalite perkelti klasės į numatytąjį konstruktorių (taigi Foo yra galutinis). Jei iš tikrųjų reikia numatytojo konstruktoriaus, visada galite pridėti setter metodą, bet tada turite prisiminti jį vėliau paskambinti.
  • Robertsono prieštaravimas ... Daugiau stulpelių nei juodos avys (nors tipo argumentų klasės nustatymas nebus tiksliai tave nužudyti). Ir, priešingai nei Robersono žodžiai, tai nepažeidžia DRY principo, nes kompiliatorius garantuos tipo teisingumą.
  • Ne visiškai patvirtintas Foo<L> įrodymas. Pirmiausia ... newInstance() skambins wobbler, jei argumento tipo klasėje nėra numatytojo konstruktoriaus. Tai taikoma visiems žinomiems sprendimams, nors bet kuriuo atveju.
  • STT metodas nėra visiškai kapsuliuojamas. Tačiau nesvarbu (atsižvelgiant į piktinančias STT išlaidas).
20
07 янв. atsakymas duotas R2D2M2 07 Jan 2013-01-07 10:11 '13, 10:11, 2013-01-07 10:11

Jūs galite tai padaryti dabar, ir jam nereikia daug atspindžių kodo.

 import com.google.common.reflect.TypeToken; public class Q26289147 { public static void main(final String[] args) throws IllegalAccessException, InstantiationException { final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {}; final String string = (String) smpc.type.getRawType().newInstance(); System.out.format("string = \"%s\"",string); } static abstract class StrawManParameterizedClass<T> { final TypeToken<T> type = new TypeToken<T>(getClass()) {}; } } 

Žinoma, jei jums reikia skambinti konstruktoriui, kuriam reikės šiek tiek apmąstymų, tačiau tai labai gerai dokumentuota, šis triukas nėra toks!

Čia yra „ TypeToken“ „JavaDoc“ .

19
08 авг. atsakymą pateikė vartotojo177800 08 rug . 2014-08-08 05:03 '14, 5:03 am 2014-08-08 05:03

Pagalvokite apie funkcionalesnį požiūrį: vietoj to, kad kuriant E iš nieko (tai yra aiškiai kodo kvapas), atlikite funkciją, kuri žino, kaip ją sukurti, t.y.

 E createContents(Callable<E> makeone) { return makeone.call(); // most simple case clearly not that useful } 
12
16 апр. atsakymas pateikiamas Ingo 16 d. 2015-04-16 19:20 '15, 19:20, 2015-04-16 19:20

Java Tutorial“ - bendrųjų funkcijų apribojimai :

Nepavyko sukurti tipų parametrų pavyzdžių

Negalite sukurti tipo parametro egzemplioriaus. Pavyzdžiui, šis kodas sukelia kompiliavimo laiko klaidą:

 public static <E> void append(List<E> list) { E elem = new E(); // compile-time error list.add(elem); } 

Kaip problemos sprendimą galite sukurti tipo parametrų objektą per atspindį:

 public static <E> void append(List<E> list, Class<E> cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); } 

Priedo metodą galite skambinti taip:

 List<String> ls = new ArrayList<>(); append(ls, String.class); 
8
13 сент. atsakymą pateikė Sergi Sokolenko 13 sep . 2012-09-13 16:12 '12, 4:12 pm 2012-09-13 16:12

Čia yra parinktis, kurią galėjau padėti:

 public static class Container<E> { private Class<E> clazz; public Container(Class<E> clazz) { this.clazz = clazz; } public E createContents() throws Exception { return clazz.newInstance(); } } 

EDIT: Arba galite naudoti šį konstruktorių (tačiau tam reikalingas E pavyzdys):

 @SuppressWarnings("unchecked") public Container(E instance) { this.clazz = (Class<E>) instance.getClass(); } 
6
16 сент. Atsakymas, kurį pateikė Mike Stone , rugsėjo 16 d. 2008-09-16 21:14 '08 9:14 val. 2008-09-16 21:14

Jei nenorite įvesti klasės pavadinimo du kartus per egzemplioriaus kūrimą, pavyzdžiui:

 new SomeContainer<SomeType>(SomeType.class); 

Galite naudoti gamyklos metodą:

 <E> SomeContainer<E> createContainer(Class<E> class); 

Kaip:

 public class Container<E> { public static <E> Container<E> create(Class<E> c) { return new Container<E>(c); } Class<E> c; public Container(Class<E> c) { super(); this.c = c; } public E createInstance() throws InstantiationException, IllegalAccessException { return c.newInstance(); } } 
6
atsakymas yra jb. Rugsėjo 17 d 2008-09-17 23:19 '08 at 11:19 pm 2008-09-17 23:19

Kai dirbate su „E“ kompiliavimo metu, jums iš tikrųjų nereikia faktinio bendrojo tipo „E“ (nesinaudojate refleksija arba dirbate su bendrinio tipo bazine klase), taigi leiskite poklasiui pateikti pavyzdį E.

 Abstract class SomeContainer<E> { abstract protected E createContents(); public doWork(){ E obj = createContents(); // Do the work with E } } **BlackContainer extends** SomeContainer<Black>{ Black createContents() { return new Black(); } } 
4
12 февр. atsakymas į Ira Ira 12 vas 2014-02-12 04:31 '14 at 4:31 2014-02-12 04:31

Deja, „Java“ neleidžia daryti to, ką norite. Žr. Oficialų sprendimą :

Negalite sukurti tipo parametro egzemplioriaus. Pavyzdžiui, šis kodas sukelia klaidą kompiliavimo metu:

 public static <E> void append(List<E> list) { E elem = new E(); // compile-time error list.add(elem); } 

Kaip problemos sprendimą galite sukurti tipo parametrų objektą, naudodami atspindį:

 public static <E> void append(List<E> list, Class<E> cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); } 

Galite paskambinti pridėjimo metodu taip:

 List<String> ls = new ArrayList<>(); append(ls, String.class); 
4
10 февр. Atsakymą pateikė Neepsnikeep 10 vasaris. 2016-02-10 15:32 '16 at 15:32 2016-02-10 15:32

Galite naudoti:

 Class.forName(String).getConstructor(arguments types).newInstance(arguments) 

Tačiau, pavyzdžiui, reikia nurodyti tikslų klasės pavadinimą, įskaitant paketus. java.io.FileInputStream . Aš tai panaudojau matematinių išraiškų analizei sukurti.

3
18 дек. Jaroslav Smid atsakymas, gruodžio 18 d. 2008-12-18 01:09 '08, 1:09 2008-12-18 01:09

Maniau, kad galėčiau tai padaryti, bet buvau labai nusivylęs: jis neveikia, bet manau, kad verta vis tiek pasidalyti.

Galbūt kas nors gali išspręsti:

 import java.> 

Ji gamina:

 Exception in thread "main" java.> 

26 eilutė yra numeris su [*] .

Vienintelis perspektyvus sprendimas yra @JustinRudd

2
03 янв. Atsakymas: Luigi R. Viggiano 03 Jan 2013-01-03 23:13 '13, 23:13, 2013-01-03 23:13

Įdėkite atsakymą @Noah.

Pakeitimo priežastis

a] Jei pakeisite užsakymą, jis yra saugesnis, jei naudojamas daugiau nei 1 bendras tipas.

b] Bendrųjų klasių tipo parašas kartais keičiasi, taigi jums nebus nustebinti nepaaiškinamų runtime išimčių.

Patikimas kodas

 public abstract class Clazz<P extends Params, M extends Model> { protected M model; protected void createModel() { Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments(); for (Type type : typeArguments) { if ((type instanceof Class)  (Model.class.isAssignableFrom((Class) type))) { try { model = ((Class<M>) type).newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } } } 

Arba naudokite vieną linijinį.

Vienos eilutės kodas

 model = ((Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance(); 
2
07 нояб. atsakymas pateikiamas zatziky 07.11 . 2014-11-07 11:13 '14 at 11:13 2014-11-07 11:13

Tikimės, kad tai nėra per vėlu padėti!

„Java“ yra tipo sauga, tik objektas gali sukurti pavyzdį.

Mano atveju negaliu perduoti parametrų „ createContents metodui. Mano sprendimas yra naudoti plėtinius vietoj visų žemiau pateiktų atsakymų.

 private static class SomeContainer<E extends Object> { E e; E createContents() throws Exception{ return (E) e.getClass().getDeclaredConstructor().newInstance(); } } 

Tai mano pavyzdys, kai negaliu perduoti parametrų.

 private static class SomeContainer<E extends Object> { E object; void resetObject throws Exception{ object = (E) object.getClass().getDeclaredConstructor().newInstance(); } } 

Naudojant atspindį sukuriama priverstinė klaida, jei išplėsite bendrąją klasę be objekto tipo. Jei norite išplėsti bendrinį tipą į objektą, šią klaidą konvertuokite laiku.

0
16 янв. atsakymą pateikė Se Song apie 16 d. 2019-01-16 12:07 '19 , 12:07 PM 2019-01-16 12:07

Čia yra patobulintas sprendimas, pagrįstas „ ParameterizedType.getActualTypeArguments , kurį jau minėjo @noah, @Lars Bohl ir kai kurie kiti.

Pirmasis nedidelis įgyvendinimo pagerėjimas. Gamykla neturėtų grąžinti egzemplioriaus, bet tipo. Kai tik grąžinate egzempliorių, naudodami Class.newInstance() sumažinate naudojimo sritį. Kadangi tik konstruktoriai be argumentų gali būti vadinami taip. Geriausias būdas yra grąžinti tipą ir leisti klientui pasirinkti, kurį konstruktorių jis nori skambinti:

 public class TypeReference<T> { public Class<T> type(){ try { ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){ throw new IllegalStateException("Could not define type"); } if (pt.getActualTypeArguments().length != 1){ throw new IllegalStateException("More than one type has been found"); } Type type = pt.getActualTypeArguments()[0]; String typeAsString = type.getTypeName(); return (Class<T>) Class.forName(typeAsString); } catch (Exception e){ throw new IllegalStateException("Could not identify type", e); } } } 

Štai keletas naudojimo pavyzdžių. @Lars Bohl parodė tik geriausią būdą, kaip išplėsti bendrąją lytį. @noah yra tik išbandant su {} . Čia pateikiami bandymai, kuriais siekiama įrodyti abu atvejus:

 import java.> 

Pastaba: galite priversti TypeReference klientus visuomet naudoti {} kuriant pavyzdį, padarant šią klasę abstrakčią: public abstract class TypeReference<T> . Aš to nepadariau, tik norėdamas parodyti ištrintą bandomąjį atvejį.

0
28 дек. atsakymas suteiktas Aleksandro 28 d. 2018-12-28 10:53 '18, 10:53 2018-12-28 10:53

Čia yra „ createContents kuri naudoja „ TypeTools“, įgyvendinimą, kad išspręstų E šaltinio klasę:

 E createContents() throws Exception { return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance(); } 

Šis metodas veikia tik tada, kai „ SomeContainer yra poklasis, todėl faktinė E vertė yra nustatyta tipo apibrėžime:

 class SomeStringContainer extends SomeContainer<String> 

Priešingu atveju E vertė ištrinama vykdymo metu ir neatkuriama.

0
04 дек. Atsakymą pateikė Jonathan 04 Dec. 2014-12-04 23:09 '14 ne 23:09 2014-12-04 23:09

Jei turite new E() tai neįmanoma. Ir norėčiau pridurti, kad tai ne visada teisinga - kaip žinote, ar E turi atvirą konstruktorių ne-args? Bet visada galite deleguoti kūrinį kitai klasei, kuri žino, kaip sukurti egzempliorių - tai gali būti Class<E> arba jūsų paties kodas, kaip šis

 interface Factory<E>{ E create(); } class IntegerFactory implements Factory<Integer>{ private static int i = 0; Integer create() { return i++; } } 
0
16 сент. Atsakyti Pavel Feldman Sep 16 2008-09-16 21:42 '08 at 9:42 pm 2008-09-16 21:42
 return (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(); 
0
22 марта '11 в 12:59 2011-03-22 12:59 atsakymas pateikiamas Rachid kovo 22 d. 11 val. 12:59 2011-03-22 12:59

Tai galite pasiekti naudodami šį fragmentą:

 import java.> 
0
06 апр. atsakymas duotas bogdan 06 apr. 2012-04-06 14:26 '12, 12:26 PM 2012-04-06 14:26

Kaip minėjote, negalite to padaryti dėl ištrintų stilių. Tai galite padaryti su refleksija, tačiau tai reikalauja daug kodo ir daug klaidų.

0
16 сент. Atsakymą Adomo Rozenfildas pateikė rugsėjo 16 d. 2008-09-16 21:06 '08 at 9:06 pm 2008-09-16 21:06

Yra įvairių bibliotekų, kurios gali išspręsti E , naudodami metodus, panašius į tuos, kurie aptarti Robertsono straipsnyje. Čia pateikiamas „ createContents “ įgyvendinimas, kuris naudoja „ TypeTools“, kad išspręstų „E“ atstovaujamą žaliavinę klasę:

 E createContents() throws Exception { return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance(); } 

Tai daroma prielaida, kad getClass () leidžia naudoti „SomeContainer“ poklasį ir neveiks kitaip, nes faktinė parametrų reikšmė E bus ištrinta vykdymo metu, jei ji nebus parašyta į poklasį.

0
12 июня '14 в 2:42 2014-06-12 02:42 Atsakymą davė Jonathanas birželio 14 d. 14:42 2014-06-12 02:42

Galima su klasės krautuvu ir klasės pavadinimu, su kai kuriais parametrais.

 final ClassLoader classLoader = ... final Class<?> aClass = classLoader.loadClass("java.> 
-1
04 февр. atsakymas pateikiamas Roald 04 vasaris. 2014-02-04 16:00 '14 16:00 val. 2014-02-04 16:00

Žr. Kitus klausimus apie „ žymų sąrašą arba Užduokite klausimą