I have a set of classes that extend some base entity. Classes in this set may also extend from each other creating a nested hierarchy.
My goal is for all classes to have access to a method that creates a new instance of themselves. I want to implement this method in my base entity, so that all extending classes inherit this.
Here are three example classes defined to my pattern:
What Is Generic In Java
BaseEntity.java
Collection.java
Document.java
With this setup, I want to be able to do something like this:
However note that there is a compiler error in the default constructor for
Collection.java . I'm not sure why this is being caused, and I think this is also causing the compiler errors in the sample main method. What am I doing incorrectly and how do I resolve this?
Note that this a contrived example pertaining to a bigger problem I'm trying to solve. I understand that this implementation by itself looks silly.
user2066880user2066880
1,92577 gold badges2929 silver badges5151 bronze badges
2 AnswersCollection<E..> is a generic type, but your Collection c is a raw type. That means that all of its methods will be treated as raw types, which means they'll return the erasure of any generic that's there.
Your base class is declared as
BaseEntity<E extends BaseEntity> , which means that in this method:
the erasure is
That means that
c.getNewInstance() returns a BaseEntity , not a Collection , which is where your compilation error comes in.
Document , on the other hand, is not a generic class. That means that the erasure doesn't matter at compile time (for these purposes), and that getNewInstance() returns the type E represents, which in this case is Document . As such, d.getNewInstance() has a return type of Document , and so that line compiles fine.
As an aside: whenever you have recursive generics, you should make sure to account for the generic in the recursion. For instance, in this line:
you've defined yshavityshavit
BaseEntity as a generic class -- but then immediately ignored its generic in E extends BaseEntity . That line should instead be:
36.2k66 gold badges6868 silver badges100100 bronze badges
The problem with this constructor
Is that the superclass constructor is expecting a
Class<E> , but the class literal Collection.class is a Class<Collection> . These types are incompatible, because E could be a Collection , a Document , or anything else that might extend Collection .
Any class like
Document that extends Collection must supply its own class, so it will be calling the other Collection constructor that takes a Class<E> anyway, so I don't think the Collection() constructor has any use. I would remove it.
Also, in your upper bound for
E , you are using the raw form of the very classes you are attempting to make generic. Use
and
The type
Collection is generic, so you must specify the generic type parameter that matches the argument to the Collection constructor.
Document is not itself generic, so this code is still fine: Arma 3 multiplayer respawn download.
Document must be supplied as a type argument to Collection even when directly creating a Document , because a Document is a Collection<Document> .
155k2121 gold badges217217 silver badges303303 bronze badges
Not the answer you're looking for? Browse other questions tagged javagenerics or ask your own question.
Generics in Java is similar to templates in C++. The idea is to allow type (Integer, String, … etc and user defined types) to be a parameter to methods, classes and interfaces. For example, classes like HashSet, ArrayList, HashMap, etc use generics very well. We can use them for any type.
Like C++, we use <> to specify parameter types in generic class creation. To create objects of generic class, we use following syntax.
Output:
We can also pass multiple Type parameters in Generic classes.
Output:
We can also write generic functions that can be called with different types of arguments based on the type of arguments passed to generic method, the compiler handles each method.
Output :
Programs that uses Generics has got many benefits over non-generic code.
References:
https://docs.oracle.com/javase/tutorial/java/generics/why.html
This article is contributed by Dharmesh Singh. If you like GeeksforGeeks and would like to contribute, you can also write an article and mail your article to [email protected]. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above
Recommended Posts:Java Generic Class
If you create a generic class in Java (the class has generic type parameters), can you use generic methods (the method takes generic type parameters)?
Consider the following example:
As you would expect with a generic method, I can call
doSomething(K) on instances of MyClass with any object:
However, if I try to use instances of
MyGenericClass without specifying a generic type,I calling doSomething(K) returns an Object , regardless of what K was passed in:
Oddly, it will compile if the return type is a generic class - e.g.
List<K> (Actually, this can be explained - see answer below):
Also, it will compile if the generic class is typed, even if only with wildcards:
EDIT:
To clarify, I would expect an untyped or raw-typed generic class not to honour the generic class's type parameters (because they haven't been provided). However, it's not clear to my why an untyped or raw-typed generic class would mean that generic methods are not honoured.
It transpires that this issue has already been raised on SO, c.f. this question. The answers to this explain that when a class is untyped / in its raw-form, all generics are removed from the class - including typing of generic methods.
However, there isn't really an explanation as to why this is the case. So allow me to clarify my question:
EDIT - discussion of JLS:
It has been suggested (in answer to the previous SO question and to this question) that this is treated in JLS 4.8, which states:
The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.
It is clear to me how this relates to an untyped class - the class generic types are replaced with the erasure types. If the class generics are bound, then the erasure type corresponds to those bounds. If the they are not bound, then the erasure type is Object - e.g.
Whilst generic methods are instance methods, it is not clear to me that JLS 4.8 applies to generic methods. The generic method's type (
<K> in earlier example) is not untyped, as it's type is determined by the method parameters - only the class is untyped / raw-typed.
Community♦
amaidmentamaidment
4,28733 gold badges3939 silver badges8181 bronze badges
6 Answers
'for backwards compatibility' seems a sufficient reason for the type erasure of class generic types - it is needed e.g. to allow you to return an untyped List and pass it to some legacy code. The extension of this to generic methods seems like a tricky sub-case.
The JLS snippet from 4.8 (which you quote) covers constructors, instance methods and member fields - generic methods are just a particular case of instance methods in general. So it seems your case is covered by this snippet.
Adapting JLS 4.8 to this specific case :
The type of a generic method is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.
(here the 'type' of the method would include all parameter and return types). If you interpret 'erasure' as 'erasing all generics', then this does seem to match the observed behaviour, although it is not very intuitive or even useful. It almost seems like an overzealous consistency, to erase all generics, rather than just generic class parameters (although who am I to second guess the designers).
Perhaps there could be problems where the class generic parameters interact with the method generic parameters - in your code they are fully independent, but you could imagine other cases where they are assigned / mixed together. I think it's worth pointing out that use of raw types are not recommended, as per the JLS :
The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types
Some of the thinking of the java developers is apparent here : Aimersoft drm media converter free full version download.
(bug + fix showing that a method's return type is treated as part of the method's type for the purposes of this type erasure)
There is also this request, where someone appears to request the behaviour you describe - only erase the class generic parameters, not other generics - but it was rejected with this reasoning:
The request is to modify type erasure so that in the type declaration
Foo<T> , erasure only removes T from parameterized types. Then, it so happens that within Map<K,V> 's declaration, Set<Map.Entry<K,V>> erases to Set<Map.Entry> .
But if
Map<K,V> had a method that took type Map<String,V> , its erasure would just be Map<String> . For type erasure to change the number of type parameters is horrific, especially for compile-time method resolution. We are absolutely not going to accept this request.
It is too much to expect to be able to use raw types ( Paul Bellora
Map ) while still getting some of the type-safety of generics (Set<Map.Entry> ).
46.6k1717 gold badges115115 silver badges166166 bronze badges
Graham GriffithsGraham Griffiths
2,06011 gold badge77 silver badges1212 bronze badges
With Java Generics, if you use the raw form of a generic class, then all generics on the class, even unrelated generic methods such as your
makeSingletonList and doSomething methods, become raw. The reason as I understand it is to provide backwards compatibility with Java code written pre-generics.
If there is no use for your generic type parameter rgettmanrgettman
T , then simply remove it from MyGenericClass , leaving your methods generic with K . Else you'll have to live with the fact that the class type parameter T must be given to your class to use generics on anything else in the class.
155k2121 gold badges217217 silver badges303303 bronze badges
I've found one reason to fully discard generics (however it's not quite good). Reason is: generics may be bounded. Consider this class:
The compiler has to discard generics in
doSomething , when you use the class without generics.And I think all generics are discarded to be consistent with this behavior.
makeSingletonList compiles because Java does an unchecked cast from List to List<K> (however compiler displays warning).
3,38733 gold badges2727 silver badges5858 bronze badges
MAnyKeyMAnyKey
This doesn't answer the fundamental question, but does address the issue of why
makeSingletonList(..) compiles whilst doSomething(..) does not:
In an untyped implementation of
MyGenericClass :
..is equivalent to:
This will compile (with several warnings), but is then open to runtime errors.
Indeed, this is also analogous to:
..which compiles but will throw a runtime amaidmentamaidment
ClassCastException when you try and get the String value out and cast it to an Integer .
4,28733 gold badges3939 silver badges8181 bronze badges
The reason for this is backwards compatibility with pre-generics code. The pre-generics code did not use generic arguments, instead using what seems today to be a raw type. The pre-generics code would use
Object references instead of references using the generic type, and raw types use type Object for all generic arguments, so the code was indeed backwards compatible.
As an example, consider this code:
This is pre-generic code, and once generics were introduced, this was interpreted as a raw generic type equivalent to:
Because the ? doesn't have an
extends or super keyword after it, it is transformed into this:
The version of tbodttbodt
List that was used before generics used an Object to refer to a list element, and this version also uses an Object to refer to a list element, so backwards compatibility is retained.
12k44 gold badges4545 silver badges6969 bronze badges
From my comment on MAnyKeys answer:
I think the full type erasure makes sense combined with the mentioned backwards compatability. When the object is created without generic type arguments it can be (or shoudl be?) assumed that the usage of the object takes place in non generic code.
Consider this legacy code:
called like this:
Now the class and it's method are made generic:
Now the above caller code wouldn't compile anymore as
NotKType is not a suptype of KType . To avoid this the generic types are replaced with Object . Although there are cases where it wouldn't make any difference (like your example), it is at least very complex for the compiler to analyze when. Might even be impossible.
I know this szenario seems a little constructed but I'm sure it happens from time to time.
André StannekAndré Stannek
Not the answer you're looking for? Browse other questions tagged javagenericslanguage-designgeneric-methodraw-types or ask your own question.
There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.
To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound.
Example
Following example illustrates how extends is used in a general sense to mean either 'extends' (as in classes) or 'implements' (as in interfaces). This example is Generic method to return the largest of three Comparable objects −
Live Demo
This will produce the following result −
OutputComments are closed.
|
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories |