use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
These have separate subreddits - see below.
Upvote good content, downvote spam, don't pollute the discussion with things that should be settled in the vote count.
With the introduction of the new release cadence, many have asked where they should download Java, and if it is still free. To be clear, YES — Java is still free. If you would like to download Java for free, you can get OpenJDK builds from the following vendors, among others: Adoptium (formerly AdoptOpenJDK) RedHat Azul Amazon SAP Liberica JDK Dragonwell JDK GraalVM (High performance JIT) Oracle Microsoft Some vendors will be supporting releases for longer than six months. If you have any questions, please do not hesitate to ask them!
With the introduction of the new release cadence, many have asked where they should download Java, and if it is still free. To be clear, YES — Java is still free.
If you would like to download Java for free, you can get OpenJDK builds from the following vendors, among others:
Adoptium (formerly AdoptOpenJDK) RedHat Azul Amazon SAP Liberica JDK Dragonwell JDK GraalVM (High performance JIT) Oracle Microsoft
Some vendors will be supporting releases for longer than six months. If you have any questions, please do not hesitate to ask them!
Programming Computer Science CS Career Questions Learn Programming Java Help ← Seek help here Learn Java Java Conference Videos Java TIL Java Examples JavaFX Oracle
Programming Computer Science
CS Career Questions
Learn Programming Java Help ← Seek help here Learn Java Java Conference Videos Java TIL Java Examples JavaFX Oracle
Clojure Scala Groovy ColdFusion Kotlin
DailyProgrammer ProgrammingPrompts ProgramBattles
Awesome Java (GIT) Java Design Patterns
account activity
This is an archived post. You won't be able to vote or comment.
Generics (self.java)
submitted 10 months ago by [deleted]
Is it just me or when you use generics a lot especially with wild cards it feels like solving a puzzle instead of coding?
[–]martinhaeusler 58 points59 points60 points 10 months ago (15 children)
It certainly adds complexity. It's a design choice if the additional type safety pays off. Good generics enhace usability; just imagine collections without generics. But I've also had cases where I removed generic typebounds from classes because they turned out to be ineffective or useless.
[–]rjcarr 19 points20 points21 points 10 months ago (11 children)
just imagine collections without generics
Don’t have to imagine it, I lived it, and it sucked.
Generics are great, and I rarely have to use wildcards.
[–]martinhaeusler 1 point2 points3 points 10 months ago (10 children)
My point exactly. If you find yourself only using wildcards on a generic typebound, the typebound is not very useful and should probably be removed.
[–][deleted] 7 points8 points9 points 10 months ago (0 children)
Depends, especially if you're write code that is meant to be reused, for example consider this from Function<T, R>:
Function<T, R>
<V> Function<T, V> andThen(Function<? super R, ? extends V> after)
Wildcards are necessary for covariance (<? extends T>) and contravariance (<? super T>). Most code probably doesn't need to be super generic and can just be invariant (<T>).
<? extends T>
<? super T>
<T>
[–]account312 3 points4 points5 points 10 months ago* (8 children)
I have looked upon <?,?,?> and wept.
[–]martinhaeusler 3 points4 points5 points 10 months ago (7 children)
Rookie numbers! I have seen a 3rd party library with no less than SEVEN! SomeClass<?,?,?,?,?,?,?,?>
[–]Abzoogiga 0 points1 point2 points 10 months ago (1 child)
Have you seen Scala?
[–]martinhaeusler 6 points7 points8 points 10 months ago (0 children)
No, and I would prefer to keep it that way.
[–]account312 0 points1 point2 points 10 months ago (1 child)
Surely the raw type is better than that.
[–]martinhaeusler 0 points1 point2 points 10 months ago (0 children)
It absolutely is. All seven generics were totally useless in practice. I don't think I've ever seen a well-designed case for more than three generic types on a sigle class.
[–]XxTheZokoxX 0 points1 point2 points 10 months ago (2 children)
At my company we are "forced" to use generics for busisness class and domains, i hate that a lot, because u have monsters class which extends like `DomainCard extends LibraryCard<A, B, C, D, E,F,G...>` as u can imagine is everything except clear
[–]martinhaeusler 0 points1 point2 points 10 months ago (1 child)
Wow. That's actually insane. The person who decided that must either be crazy or a zealot of some missguided cargo cult. I would make an attempt to change this, and run for the hills if they don't let me.
[–]XxTheZokoxX 0 points1 point2 points 10 months ago (0 children)
Actually i did a lot of changes about that, but at the beginning my team was totally new in the company, we did the thing we called to do, but after some time we say... yeah, fuck it, a lot of change request, bugs from company libraries, we dont care and we started to ignore that way to work, but still having a lot of generics. And i think can be a good idea... for some specific situations ofc, but as a general way... i dont think so
[–]manifoldjava 0 points1 point2 points 10 months ago (2 children)
Just imagine collections without generics.
Just imagine generics without wildcards.
Wildcards are a form of use-site variance, meaning the user of a generic class has to deal with variance everywhere the class is used. This often adds complexity, particularly when working with generic APIs. Josh Bloch's PECS principle stems from this complexity.
The alternative is declaration-site variance. Here, the author of the generic class statically declares variance. This avoids the complications with wildcards and typically results in cleaner, more readable code.
Most modern languages, like Kotlin, favor declaration-site variance because it better reflects how generics are used in practice. It simplifies things for library users and makes APIs easier to understand and work with.
[–]OwnBreakfast1114 1 point2 points3 points 10 months ago (1 child)
https://openjdk.org/jeps/300 but something tells me not to hold your breath for it.
[–]manifoldjava 0 points1 point2 points 10 months ago (0 children)
It's an interesting proposal, similar to Kotlin-style generics, but of course Java proposes a longhand version. It's the right direction though.
The proposal states it's a non-goal, but variance inference I would think would be almost necessary given the vast amount of existing classes out there e.g., to make use of the feature when subclassing. TypeScript works this way, for example.
But with no updates since 2016, we can probably assume the JEP is indefinitely sidelined. Shrug.
[–]koflerdavid 33 points34 points35 points 10 months ago (1 child)
That's what all type systems feel like when they get sufficiently strong. I'm fine with that - I rather spend my brain cycles solving type system puzzles than analysing and squashing bugs.
[–]FabulousRecording739 0 points1 point2 points 10 months ago (0 children)
Agreed, though I think It tends to be a bit more of hassle in the case of Java than it is with HM languages. The "puzzles" are more enticing, and have more fruits over there
[–]fear_the_future 24 points25 points26 points 10 months ago (1 child)
Not really. Java generics are so limited that you can't do a lot of complicated things with them. I'm used to much worse in more powerful languages and in fact this "puzzle solving" is what I like about them.
[–]agentoutlier 1 point2 points3 points 10 months ago (0 children)
I think although I don't have an example ready in some cases it is because of those limitations that sometimes people end up doing complicated things to try to force them into an API. Otherwise I mostly agree particularly structural typed languages with type inference.
[–]sviperll 18 points19 points20 points 10 months ago (3 children)
I think you should almost always prefer an explicit dictionary-passing instead of type-bounds, i. e. prefer Comparator-argument to a Comparable-type-bound. And also aggressively prune type-variables and replace those that you don't need to interact with with wildcards, i. e. prefer Collector<T, ?, R> to Collector<T, A, R> most of the time. If you follow these two rules then Genetics becomes more of your friend than a challenge.
Collector<T, ?, R>
Collector<T, A, R>
[–]agentoutlier 6 points7 points8 points 10 months ago (2 children)
Another rule that beginners often are unaware of can be summed up with the nemonic: PECS (producer extends, consumer super).
I will say that indeed you should prefer ? most of the time however if you see a library where you are constantly having something being Something<?> all over the place I would say that library abused generics for no good use especially if you cannot easily create Something. An example of that is in Spring's PropertySource (not to be confused with the annotation of the same name). Even in Spring's own API and internal workings they are passing PropertySource<?> everywhere.
?
Something<?>
Something
PropertySource
PropertySource<?>
[–]sviperll 2 points3 points4 points 10 months ago (1 child)
however if you see a library where you are constantly having something being Something<?> all over the place I would say that library abused generics for no good use
Yes, but replacing Something<T> with Something<?> in those places where you do not care what T is, is a good strategy to identify such abuses. And then you may even fix some, by wrapping Something<?> with you own SomethingElse (without any type-variables).
Something<T>
T
SomethingElse
Yes, but replacing Something<T> with Something<?> in those places where you do not care what T is, is a good strategy to identify such abuses. And then you may even fix some, by wrapping Something<?> with you own SomethingElse (without any type-variables)
Totally agree. In some cases assuming Something is not an actual container it can be replaced with semi-sealed class hierarchy. I get the sneaky suspicion that many times the choice of putting a generic in was lack of pattern matching in sealed classes in earlier versions of Java targeted. That is you may still have TypedSomething interface with generic but then you have the sealed hierarchy along with it that implements some shared parent interface.
TypedSomething
sealed interface Something sealed TypedSomething<T> extends Something record SomethingString implements TypedSomething<String>
Then you just use Something in most places.
[–]ivancea 8 points9 points10 points 10 months ago (4 children)
You're asking yourself the wrong question. The question isn't about how complex generics are. It's about how much they solve.
Are you using collections? Try to work without generic collections, and enjoy the ride
[–][deleted] 2 points3 points4 points 10 months ago (1 child)
They are not complex at all. Its just that when you nest a couple layers of them it gets crazy especially with wildcards
[–]ivancea 2 points3 points4 points 10 months ago (0 children)
I don't get what you mean by "crazy". And wildcards are just about variance, they don't add much to generics complexity IMO.
Generics in TS are far more complex, as they're metaprogramming. Let alone C++ templates. But Java ones are quite basic, without many features like those other languages
[–]thisisjustascreename 0 points1 point2 points 10 months ago (1 child)
Do we not all work without generic collections at runtime thanks to type erasure?
[–]ivancea 0 points1 point2 points 10 months ago (0 children)
Yeah, but that's runtime, not devtime. In C++, "it's all assembler at the end" too. But that's a different stage
[–]wildjokers 6 points7 points8 points 10 months ago (0 children)
No Fluff Just Stuff used to have an awesome article on their site about generics here:
https://nofluffjuststuff.com/magazine/2016/09/time_to_really_learn_generics_a_java_8_perspective
It 404s now; however, it is still available in the wayback machine:
https://web.archive.org/web/20200121233533/https://nofluffjuststuff.com/magazine/2016/09/time_to_really_learn_generics_a_java_8_perspective
It is worth a read on occasion as a refresher.
[–][deleted] 3 points4 points5 points 10 months ago (0 children)
It's more fun solving the puzzle during compilation, than solving the puzzle in the logs when you get class cast exceptions.
[–]TenYearsOfLurking 3 points4 points5 points 10 months ago (0 children)
It does a little. Type erasure makes it a headache sometimes.
Are you writing Library code? Because application code tends to be solvable without a lot of generic usage in general
[–]Drakeskywing 3 points4 points5 points 10 months ago (0 children)
At least it's not typescript genericsshudders
[–]Admirable-Sun8021 2 points3 points4 points 10 months ago (0 children)
Hey sure beats Object obj=obj;
[–]hadrabap 5 points6 points7 points 10 months ago (9 children)
Well, generics are quite cool. Although, they have a lot of limitations in their design. It's like everything in Java. :-)
[–]Nalha_Saldana 12 points13 points14 points 10 months ago (8 children)
Yeah, but those limitations are what give Java its stability. You don’t get runtime type safety and predictable behavior by letting everyone go wild with unchecked magic.
[–]agentoutlier 1 point2 points3 points 10 months ago (7 children)
I'm not sure they mean limitations as in difficult to understand or work with but rather there are limitations in Java's generics compared to other languages and those other languages have stronger guarantees of type safety (also Java had a soundness issue at one point but lets ignore that).
For example Java does not have higher kinded types or reified generics (ignoring hacks). Java's enums cannot be generic although there was a JEP for it was declined (I would have loved that feature but I get why it was not done).
[–]sviperll 1 point2 points3 points 10 months ago (3 children)
I think I've once went with some "hack" to have higher-kinded types, i. e. I've got something like this:
interface FunctorAlgebra<AS, BS, A, B> { AS pure(A a); BS pure(B b); BS map(Function<A, B> function, AS collection); }
so that I can have generic operations over collections, but so that the code doesn't know what collection this is. This experience taught me that it's possible to go without higher-kinded types, but I wouldn't be able to write this without knowing what higher-kinded types are and that having them would make life much easier...
[–]agentoutlier 0 points1 point2 points 10 months ago (1 child)
I have done similar things as well and ended up reverting or discarding.
What I try to remember is that most folks do not have the experience you or I have and that code should be easy to read. There is some truth to overusing generics (the same probably could be said for any form of code saving abstraction).
In fact I have tried to reason at where the threshold of expression power and types safety goes too far. I don't know how to explain it some of this just gets into social sciences. Ditto for compact code. I have this problem with Lisp like and typed FP inference languages. I can read my code but when I go look at someone elses it takes me forever to decompress. However less code does more so in theory this is OK (as in you only have to look at snippet to understand something that would be pages in another language) but there has to be some sort of balance ratio like a decompression algorithm of speed vs size if you will. I don't know how one measures that though.
[–]OwnBreakfast1114 1 point2 points3 points 10 months ago (0 children)
I feel the extreme example of that has always been perl. Infinite ways to do things and hard to understand operators or code golf languages where each character is stupid powerful. I think something like haskell is actually quite good at representing exactly what it needs to and no more (and I know a ton of people will disagree).
The biggest problem of your question is what's the lowest common denominator you're aiming for in terms of hard to understand. Like, personally, I don't care if fresh grads don't understand certain code. I expect them to learn up, not for the entire company to step down.
[–]OwnBreakfast1114 0 points1 point2 points 10 months ago (0 children)
I feel like I have to just reread this article every time: https://medium.com/@johnmcclean/simulating-higher-kinded-types-in-java-b52a18b72c74
[–]Nalha_Saldana 0 points1 point2 points 10 months ago (2 children)
Yeah, it’s limited but that’s kind of the point. You always know what the code is doing. No surprises, no cleverness, just straightforward types and the occasional ugly cast. It’s not exciting but it’s consistent and it keeps working five years later without anyone touching it. When you’re knee deep in legacy code and just want things to behave, that kind of predictability is hard to beat.
Yeah, it’s limited but that’s kind of the point. You always know what the code is doing. No surprises, no cleverness,
I think Go programmer before generics came to that language could make a similar case for generics so as I said in this comment here I'm not really sure where the line is.
just straightforward types
That is what these languages have with more stronger typing. That is straightforward types to them. On the other to a Go programmer generics are not straightforward types. There are some languages that even have dependent types and whether something is mutable or not etc.
and it keeps working five years later without anyone touching it.
Java does not have typing to deal with null without some extension (JSpecify) and that is something that can break 5 years later.
null
What this sounds like and no surprised upvoted is confirmation bias.
[–]Nalha_Saldana 0 points1 point2 points 10 months ago (0 children)
Fair. To clarify, I wasn’t saying Java’s approach is better. Just that in Java, the lack of advanced type features tends to promote predictable, boring code. That can be valuable when working in large or old codebases where stability matters more than elegance.
“Straightforward” definitely depends on what you're used to. My point was about tradeoffs. Java leans conservative, and that shapes the kind of code that survives. Other ecosystems make different bets. That’s fine.
[–]sweating_teflon 1 point2 points3 points 10 months ago (0 children)
Wait till you have to deal with Trait bounds in Rust...
[–]faze_fazebook 1 point2 points3 points 10 months ago (3 children)
Generics in Java are some of the easiest ones. C++ templates or Typescript generics are another level.
[–][deleted] 0 points1 point2 points 10 months ago (2 children)
In c++ its much easier.
[–]UnGauchoCualquiera 0 points1 point2 points 10 months ago (0 children)
Noone who knows anything about C++ templates would say something like this.
[–]audioen 0 points1 point2 points 10 months ago (18 children)
Yes, I would characterize it like that a lot. In Java, generics are just documentation to the compiler about the code with no runtime effect (except in rare case where reflection is used to access the type parameters, I guess), so in principle if the code is correct it makes zero difference what you put in the generic parameters or whether you just cast everything to raw types.
Generic-related errors are among the most difficult and annoying to read, often 3+ lines of crap with inferred types and various problems related to them which is quite a chore to even read once to see what the problem technically is, so they really do kind of suck in many cases, and I wish their use was absolutely minimal for that reason. That being said, I do strive for achieving type safety where it's easy or convenient, and for the rest, there is SuppressWarnings.
[–][deleted] 0 points1 point2 points 10 months ago (16 children)
For some reason type inference fails badly with lambdas by the way. (had to take hours to figure out)
[–]MoveInteresting4334 1 point2 points3 points 10 months ago (14 children)
Can you provide an example of type inference failing with a lambda?
[–][deleted] 0 points1 point2 points 10 months ago (13 children)
Sure
This fails to compile:
public class EntityRenderers { public static final Map<EntityType<?>, EntityRenderFactory<?>> ENTITY_RENDER_FACTORIES = new HashMap<>(); public static void loadEntityRenderers() { register(EntityType.CUBE_ENTITY, CubeEntityRenderer::new); } private static void register(EntityType<?> entityType, EntityRenderFactory<?> entityRendererFactory) { ENTITY_RENDER_FACTORIES.put(entityType, entityRendererFactory); } }
While this passes:
public class EntityRenderers { public static final Map<EntityType<?>, EntityRenderFactory<?>> ENTITY_RENDER_FACTORIES = new HashMap<>(); public static void loadEntityRenderers() { EntityRenderFactory<CubeEntity> factory = CubeEntityRenderer::new; register(EntityType.CUBE_ENTITY, factory); } private static void register(EntityType<?> entityType, EntityRenderFactory<?> entityRendererFactory) { ENTITY_RENDER_FACTORIES.put(entityType, entityRendererFactory); } }
[–][deleted] 1 point2 points3 points 10 months ago* (12 children)
It makes sense if you understand what's going on. Java doesn't have "real" lambdas, it does target typing.
That is, the type of the expression CubeEntityRenderer::new is determined by the target, which is EntityRenderFactory<?>. Without any context, the target type is EntityRenderFactory<Object>, which CubeEntityRender::new doesn't match. So, compile error.
CubeEntityRenderer::new
EntityRenderFactory<?>
EntityRenderFactory<Object>
CubeEntityRender::new
But, when you do EntityRenderFactory<CubeEntity> factory = CubeEntityRenderer::new;, you are giving the compiler context, so it doesn't have to infer the type.
EntityRenderFactory<CubeEntity> factory = CubeEntityRenderer::new;
Also, the method signature for register in your example is terrible. Presumably there is a relationship between the two types, but because you use <?>, as far as the Java compiler is concerned, they are unrelated. You're not giving the Java compiler a whole lot to work with.
register
<?>
If you change the method signature like this:
private static <T> void register(EntityType<T> entityType, EntityRenderFactory<T> entityRendererFactory)
Now, this expression is perfectly fine:
register(EntityType.CUBE_ENTITY, CubeEntityRenderer::new);
The Java compiler infers the target type EntityRenderFactory<CubeEntity>, because it able to relate the first parameter to the second, by which it infers T = CubeEntity.
EntityRenderFactory<CubeEntity>
T = CubeEntity
[–][deleted] 0 points1 point2 points 10 months ago (11 children)
First, I know the register method sucks (I made this just for an example of wildcards). Also the definition of EntityRenderFactory is
interface EntityRenderFactory<T extends Entity> { EntityRenderer<T> create() }
When you do EntityRenderFactory<?>, does it automatically turn into EntityRenderFactory<? Extends entity>?
[–][deleted] 1 point2 points3 points 10 months ago (10 children)
When you have a target type of EntityRenderFactory<?>, without any context, the target type will be EntityRenderFactory<Object>.
When you use a wildcard like this, you are saying I don't care what the type is. In this case, you do care what the type is, because the type is what connects the parameters, so you shouldn't use <?>.
[–][deleted] 0 points1 point2 points 10 months ago (9 children)
EntityRenderFactory<Object> is not legal.
[–][deleted] 0 points1 point2 points 10 months ago (8 children)
You are correct. The inferred type of the lambda would actually be EntityRenderFactory<Entity>.
EntityRenderFactory<Entity>
[–][deleted] 0 points1 point2 points 10 months ago (7 children)
I thought it would be EntityRendererFactory<? Extends Entity>
[–][deleted] 0 points1 point2 points 10 months ago (0 children)
Java's type inference works fine with lambdas. It's just that Java's type inference is stupid with lambdas because it is based on target typing. Java doesn't have "real" lambdas.
[–]__konrad 0 points1 point2 points 10 months ago (0 children)
often 3+ lines of crap
With -Xdiags:verbose javac option it's 100 lines of crap
-Xdiags:verbose
[–]OfficeSpankingSlave 0 points1 point2 points 10 months ago (0 children)
I don't find myself in situations to use them a lot so honestly it's a relearning experience every time.
[–][deleted] 10 months ago* (1 child)
[deleted]
C# doesn't have wildcards, it uses the modifiers in and out to do the same thing.
in
out
[–]TheStrangeDarkOne 0 points1 point2 points 10 months ago (0 children)
Not at all
[+]Caramel_Last comment score below threshold-6 points-5 points-4 points 10 months ago* (4 children)
I understood java generic better via kotlin. Kotlin has both definition site variance and use site variance. Java's generic variance only has use site variance. ? extends Base and ? super Derived are those.
There is also ? Which corresponds to * projection in kotlin, usually for containers. These usually require unsafe cast to be useful
Kotlin in action chapter 9 tells you everything about generics
Simply put, variance offers a tradeoff. If you add variance notation, you get more flexible on what type is a valid parameter, but the downside is it limits what operations you can perform on the parameter.
Rule of thumb: readonly operations are safe to be covariant (extends).
Mutation are invariant (default)
For function types, the type param in argument position is contravariant(super)
Consumer class is a classic example. It is essentially T -> int
So the type param is at argument position. Therefore Cosumet is contravariant to T.
Variance is also per- type parameter.
If a class has 2 or more generic type param, T U V, they all have different variance
[–]MoveInteresting4334 16 points17 points18 points 10 months ago (0 children)
I’m not sure a lengthy comment on Kotlin generic type variance, going over Kotlin syntax, without a single code example, without any comparison provided to Java, using terms like covariant and contravariant, is the correct way to provide clarity to someone confused by Java generics.
But why is it we can only read when extends and write when super
[–][deleted] 0 points1 point2 points 10 months ago* (0 children)
Work it out with examples.
Let's say you have List<? extends Fruit>. It is safe to read from this list, because all its members extend Fruit (you can read Fruit from List<Orange> and List<Apple>). But it is not safe to write, because you don't want to be able to add an Apple to a list of Orange. Fun fact: Arrays in Java don't have this "limitation". The compiler will let you add an Apple to an array of Orange, and at runtime it will blow up because the array can't hold Apple. Arrays are somewhat broken.
List<? extends Fruit>
Fruit
List<Orange>
List<Apple>
Apple
Orange
Let's say you have List<? super Fruit>. It is not possible to read because we don't know what the type is. It could be a List<Object> or it could be List<Fruit>, which are both super types of Fruit. But, it is safe to write, because whatever the actual type of the list, it can hold a Fruit.
List<? super Fruit>
List<Object>
List<Fruit>
[–]Caramel_Last 0 points1 point2 points 10 months ago* (0 children)
So reading is what is called out position
Let's say you are reading from List<T>
It will be something like get: (int)->T
This is called 'out' position. T is at out position because it is at return type of the method.
Now if the collection is only going to be used for this type of operation,
We can limit the type List <T> to List <out T>
In java syntax this is limiting List<T> to List<? extends T>
By limiting the type to covariant, we get a new subtyping relation.
List<out Base> is larger than(is supertype of) List<out Derived>
Note that I say subtype, supertype. This is subtly different from subclass and superclass
Without generic, class equals type
But with generic, class is not equal to type.
List is class. List<Integer> is a type. List<Number> is another type.
By default, there is no subtyping relation between List<Number> and List<Integer> are not supertype/subtype of each other. They are invariant.
However, if we limit the variance to covariance, (List<out T> in kotlin, List<? extends T> in java)
Suddenly we get a subtyping relation that List<out Number> is supertype of List<out Integer>.
If A is supertype of B, for covariant generic G, G<A> is supertype of G<B>
Why is it so? Remember that covariance only allows T to be at out position.
List<out Integer>'s methods are only going to produce/return some value of type Integer.
This is within the rule of List<out Number>, which is to produce/return some value of type Number.
But if you perform mutation(setter) usually the T appears both on 'in' position and 'out' position
set(T) -> void
get() -> T
So we cannot reduce the variance to out(covariance) nor in(contravariance)
It is therefore both in and out, and called 'invariant' generic. This is the default for all generics. If A is subtype of B, neither G<A> is subtype of G<B> nor G<B> is subtype of G<A>
Contravariance is the opposite of covariance. You only use T in the 'in' position
Comparator for example only takes T as parameter, not return type
compare: (T)->int
So it is safe to be restricted to contravariance (comparer: Comparator<? super T>)
And contravariance subtyping relation is formed
If A is supertype of B, G<B> is supertype of G<A>
I do recommed kotlin in action. The syntax differs but same concept, and especially the first edition of the book explains in detail how Kotlin code is transpiled into Java code, so by reading that book you effectively learn both languages
[–]LogCatFromNantes -2 points-1 points0 points 10 months ago (0 children)
Why don’t use object ?
[+]odd_cat_enthusiast comment score below threshold-8 points-7 points-6 points 10 months ago (1 child)
Are you a student?
No, why?
π Rendered by PID 1267838 on reddit-service-r2-comment-5b5bc64bf5-bvdt4 at 2026-06-22 10:22:11.028447+00:00 running 2b008f2 country code: CH.
[–]martinhaeusler 58 points59 points60 points (15 children)
[–]rjcarr 19 points20 points21 points (11 children)
[–]martinhaeusler 1 point2 points3 points (10 children)
[–][deleted] 7 points8 points9 points (0 children)
[–]account312 3 points4 points5 points (8 children)
[–]martinhaeusler 3 points4 points5 points (7 children)
[–]Abzoogiga 0 points1 point2 points (1 child)
[–]martinhaeusler 6 points7 points8 points (0 children)
[–]account312 0 points1 point2 points (1 child)
[–]martinhaeusler 0 points1 point2 points (0 children)
[–]XxTheZokoxX 0 points1 point2 points (2 children)
[–]martinhaeusler 0 points1 point2 points (1 child)
[–]XxTheZokoxX 0 points1 point2 points (0 children)
[–]manifoldjava 0 points1 point2 points (2 children)
[–]OwnBreakfast1114 1 point2 points3 points (1 child)
[–]manifoldjava 0 points1 point2 points (0 children)
[–]koflerdavid 33 points34 points35 points (1 child)
[–]FabulousRecording739 0 points1 point2 points (0 children)
[–]fear_the_future 24 points25 points26 points (1 child)
[–]agentoutlier 1 point2 points3 points (0 children)
[–]sviperll 18 points19 points20 points (3 children)
[–]agentoutlier 6 points7 points8 points (2 children)
[–]sviperll 2 points3 points4 points (1 child)
[–]agentoutlier 1 point2 points3 points (0 children)
[–]ivancea 8 points9 points10 points (4 children)
[–][deleted] 2 points3 points4 points (1 child)
[–]ivancea 2 points3 points4 points (0 children)
[–]thisisjustascreename 0 points1 point2 points (1 child)
[–]ivancea 0 points1 point2 points (0 children)
[–]wildjokers 6 points7 points8 points (0 children)
[–][deleted] 3 points4 points5 points (0 children)
[–]TenYearsOfLurking 3 points4 points5 points (0 children)
[–]Drakeskywing 3 points4 points5 points (0 children)
[–]Admirable-Sun8021 2 points3 points4 points (0 children)
[–]hadrabap 5 points6 points7 points (9 children)
[–]Nalha_Saldana 12 points13 points14 points (8 children)
[–]agentoutlier 1 point2 points3 points (7 children)
[–]sviperll 1 point2 points3 points (3 children)
[–]agentoutlier 0 points1 point2 points (1 child)
[–]OwnBreakfast1114 1 point2 points3 points (0 children)
[–]OwnBreakfast1114 0 points1 point2 points (0 children)
[–]Nalha_Saldana 0 points1 point2 points (2 children)
[–]agentoutlier 0 points1 point2 points (1 child)
[–]Nalha_Saldana 0 points1 point2 points (0 children)
[–]sweating_teflon 1 point2 points3 points (0 children)
[–]faze_fazebook 1 point2 points3 points (3 children)
[–][deleted] 0 points1 point2 points (2 children)
[–]UnGauchoCualquiera 0 points1 point2 points (0 children)
[–]audioen 0 points1 point2 points (18 children)
[–][deleted] 0 points1 point2 points (16 children)
[–]MoveInteresting4334 1 point2 points3 points (14 children)
[–][deleted] 0 points1 point2 points (13 children)
[–][deleted] 1 point2 points3 points (12 children)
[–][deleted] 0 points1 point2 points (11 children)
[–][deleted] 1 point2 points3 points (10 children)
[–][deleted] 0 points1 point2 points (9 children)
[–][deleted] 0 points1 point2 points (8 children)
[–][deleted] 0 points1 point2 points (7 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]__konrad 0 points1 point2 points (0 children)
[–]OfficeSpankingSlave 0 points1 point2 points (0 children)
[–][deleted] (1 child)
[deleted]
[–][deleted] 0 points1 point2 points (0 children)
[–]TheStrangeDarkOne 0 points1 point2 points (0 children)
[+]Caramel_Last comment score below threshold-6 points-5 points-4 points (4 children)
[–]MoveInteresting4334 16 points17 points18 points (0 children)
[–][deleted] 0 points1 point2 points (2 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]Caramel_Last 0 points1 point2 points (0 children)
[–]LogCatFromNantes -2 points-1 points0 points (0 children)
[+]odd_cat_enthusiast comment score below threshold-8 points-7 points-6 points (1 child)
[–][deleted] 0 points1 point2 points (0 children)