There are good reasons. When you begin writing IDE support you will discover that:
1. Without annotations you’ll be forced to index all static methods based on the first argument, globally.
2. Method call code completion will be ridiculous. How many static methods have String, File, List, Object(!) etc. as the first parameter? I don’t think anybody wants their code completion to be invaded like that.
The annotations provide intention. Essentially, adding an extension method is not something to be taken lightly, it requires careful consideration, weighing context, frequency, etc.
IDEs are not my strong point. Aren't they smart enough to just search static methods in the scope of the compilation unit? Okay, so they have to be indexed first I suppose, but still... Ah, I suppose you want to search for methods you haven't imported yet. Now I get the annotation stuff. We're writing code for the IDE.
Looks similar to how you can use implicit classes in Scala[0]. They were definitely convenient for writing unit and integration tests, but difficult to read/maintain. I feel like a pipe operator would be a much more useful alternative instead of syntactic sugar for presenting functions as object method calls.
Oof... looks more object-oriented hell to me. I really like the way functional programming languages separate types from functions, and I've been adopting this pattern in my Java code. Extension methods let you do this and keep your fluent api. Thanks for the link though, I didn't know about implicit classes.
We did Scala at an old job and various kinds of implicits got us into so much trouble. I think if you use them judiciously, they'll be fine, but you need quite a bit of experience and maturity to use them judiciously, which most of us lacked.
Nice, this is one of the features that makes using Kotlin with existing Java frameworks so nice. It can really cut down on the boiler plate Java is infamous for. You can define extension functions on any type, or type alias. So anything that looks like it could be shorter or is a bit repetitive, you just define an extension function to make that go away.
Kotlin is great, but it is a fairly heavy dependency if all you want is extension methods. Java has mostly caught up in many other aspects, so I'm experimenting with Java + one or two compiler hacks. Checked exceptions are my next target...
I'm curious about how you plan to work on checked exceptions.
If it's just about ignoring forced exception management, Lombok provides the @SneakyThrows annotation.
I would need something more before I add a new type of extension. Maybe if you could solve the null-check issue.
i.e. rewriting
var x = a.getFoo()?.getBar()
to
var x = a.getFoo() != null ? a.getFoo().getBar() : null
In that case I may consider it for my personal projects, and then potentially recommend it at work once it becomes more widespread and has IDE support.
I don't know yet. I'm already using some of my own variations of sneakyThrows, but what I really want is just to disable the whole damn thing in the compiler (checked exceptions are a compiler feature, not a JVM feature). I won't be changing the Java syntax though, sorry!
I think Kotlin differs far too much from Java — I don’t want all those plus features that will inevitably branch too far from the JVM/Java corresponding code, becoming its new thing.
I might want a nearly-Java language that only adds trivial syntax sugar, but are otherwise the same. Groovy sort of had a good initial idea here (being a superset of Java, though again, it has grown apart a bit since).
For me Kotlin lives in this weird limbo between these two extremes. If I want a different language on the JVM I would rather do Scala or Clojure.
I wouldn’t really leave behind the JVM ecosystem, which is much bigger and better quality than the .NET one from what I gathered (though it is still good and bigger than most other languages’). And other people’s code is the numero uno productivity boost, so it’s an important area.
I think I would be fine with a smaller ecosystem. I've seen a lot of projects that have made bad choices in libraries because there are too many. Sometimes I feel like if there was just one blessed jdk way it would be a lot simpler. Honestly, the only thing that is keeping me from .NET right now is async/await. Pron really did Java a service by pushing for virtual threads.
Typically, boiler plate implemented by a standalone function is not easily discoverable, eg. no clear convention where to put it, does not look idiomatic on some usages syntax habits, and not suggested by auto completion in top results.
Extensions are searchable by syntax and naturally in a namespace, largely diminishing the risk of put it in in a single use function that will be quickly forgotten, and might seem obscure.
The code itself might not be much shorter, but you don't introduce code looking weird, if it's re-usable it's now discoverable, and if your boilerplate contained a lot of micro adjustments on a larger glue code, this glue code is now shorter and way clearer.
Also, creating a subclass just to add extension methods is definitely boiler plate. Util classes too, though they are useful for packaging functions at least.
Subclassing may not always be possible (final class, or method) and can break class invariants. Util classes with helper methods and/or extension methods are not prone to these problems.
This would be very useful. But how are these extension methods more searchable by syntax than the 'old fashioned' static util methods?
I admit I haven't tried, but I suspect that code completion in my IDE wouldn't include these extension methods.
They're more discoverable, because typing in `object.` tells the IDE that you are looking for methods on the type of `object`. If the IDE is aware of extension methods, it can also expose them the same as instance methods.
Java specific: If you're looking for a static utility method, then `UtilClass.` will also autocomplete. However, what if the function you're looking for isn't in `UtilClass`, but `SomeOtherClass`? What if you don't even know whether a method already exists for what you're trying to accomplish, and it could possibly exist in multiple libraries? Your discoverability engine quickly becomes Google.
Extension methods have been available in C# since C# 3.0 (released 2007) and a great addition IMHO. In recent .NET versions, especially ASP.NET, they are heavily relied upon for core framework tasks. Wonder why Java never got them officially.
I can only speculate, but I think it is because extension methods can be shadowed if a vendor adds a method of the same name to the class you are extending. Of course, that depends on how you resolve methods.
In java, this would be a compile time problem, because once the class is compiled the extension method is called with `invokestatic` rather than `invokevirtual` used by class methods.
There is also a healthy fear of change in the java community that could explain it.
There is also a healthy fear of change in the java community that could explain it.
Nice jab at the end! Java (well, really Sun, then Oracle) prioritises backwards compatibility over every other goal. So, does Win32 API. Does DotNet do the same? I'm unsure.
This also explains why it took so long for Java to get lambdas. And, generics with primitive types, e.g., ArrayList<int>, via Project Valhalla. It is a super hard problem to upgrade Java without breaking old stuff. Hence, it is a multi-year R&D effort.
Yeh, backwards compatibility is one of the biggest selling points of Java. Hopefully, Fluent will show that we can change one or two lines of compiler code and not burn down the whole house. We'll see.
I think you can’t look at the past 5 years and the features that are being floated for Java 21 and future releases and think fear of change is the issue—the language is now changing rapidly.
Thanks for the link. I've been trying to find the canonical answer to this question myself. There's the philosophical standpoint: API designers should control their APIs, and the technical: poor reflective discoverability, poor discoverability through documentation, not overrideable, require ad-hoc conflict-management rules.
I like his use of the word should, because as we all know it really means won't. I should exercise more, I should stop trolling people on reddit. API designers should control their APIs.
As for the technical objections, I think the conflict-management rules hold the most weight, but this problem is no more difficult than any other in language design. "not overrideable" is a feature, not a bug (can we please give up on behavioural inheritance already?), and discoverability already sucks. I mean, I still use duckduckgo to find javadocs.
My sentiments exactly. The “API designers should control their APIs” one is my favorite and one of the more ridiculous things I’ve heard him say, which is saying something. It’s beyond humor, though, that all of his reasons are fallacies.
I appreciate the intent, but man is this like a minefield waiting to be walked over. Anyone who looks at the code will scratch their head and will not understand what is going on. An annotation would perhaps make it clearer.
Also, how about IDE support? I would argue it is at least as important as the feature itself.
Yes, IDE support would help developers find the method that is being called. This is already quite a problem given we have complex class hierarchies and interfaces with default methods. I'm not sure I see the need for an annotation though.
A compile-time annotation could at least control whether your plugin is allowed to transform the ADT in the given context (say: for a class). Otherwise is it not possible that it transforms the ADT even when the user did not intend to? Can this be a source of mysterious bugs?
The syntax tree is only transformed when instance method lookup fails. i.e. when your code doesn't compile in standard java form. You would be doing pretty well to accidentally call an extension method seeing as you either have to write them or import them.
The key point is that they do it out of the box, so everyone is aware of it. As it is, it could easily interfere with some other non-standard extension plugin, for example.
I appreciate the intent, but man is this like a minefield waiting to be walked over. Anyone who looks at the code will scratch their head and will not understand what is going on. An annotation would perhaps make it clearer.
That is the generic argument against extension methods wherever they appear. They are very confusing, until you get used to remembering their existence, then you know what to search for.
Also, how about IDE support? I would argue it is at least as important as the feature itself.
Extension methods in a language that explicitly supports them and with good IDE support are perfectly fine. I love them in Kotlin. Extension methods as a compiler plugin that rewrites the AST... not so great. Great IDE support is a large part of what makes Java worth using, the last thing I need is to break that.
The "this" that GP mentions is for for extension methods similar to the article to be regcognizeed as such (never done if the annotation isn't present), they're written such as:
static int Foo( this Bar bar, int biz) => bar.xyz+biz;
Bar bar = new Bar() { xyz=123; };
int result = bar.Foo(456); // Works even if Foo wasn't a part of Bar initially
Elsewhere annotations(called attributes in C# land) can often be used in the same way as in Java to document serialization behaviour,etc. So in practice there aren't much differences apart from how library authors haven't embraced it entirely as much for automagic bindings (partly due to philosophy, partly due to other facilities being more appropriate).
There is a massive difference between a feature universally offered by the language and something only supported by using a specific library of compiler plugin...
So, "some languages do this with annotations, others don't need it". My point stands.
I mean it's fine to not like annotations or the decision to use them, but that another language does things differently is almost a tautology and hardly worthy of note. IMO, reasonable people will disagree.
Official upgrades to languages don't result in that kind of combinational explosion of language extension combinations because the features eventually become standard. They do result in a combinational explosion of language feature combinations, though, so there's still a problem with languages just getting big. IDE's and other language tools need to support it too.
In JavaScript there is .js, .ts, .jsx, and .tsx. I hope that doesn't become a lot more, but for low amounts of variation, it's reasonable.
I think Haskell is a good example of what happens when a language supports too many extensions. Why are there so many? How do people deal with them all? Needing to pick another file extension seems like pressure to do this more sparingly?
Another java fork like Lombok. But this time the authors didn't take the time to support all java compilers/IDEs, so the situation is even worse.
If this is just to highlight the merit of static extension methods with a follow up JEP I am all for it, but please don't advertise this as a feature for production. It's a trap.
Well a language fork. But not a full implementation fork of course. Or would you argue that what Lombok does is part of the java spec? It effectively extends the java spec in non backwards compatible ways.
I did some digging on past discussions, and yes, it seems the stars are not aligned for extension methods in standard Java.
But tbh, there are some very good arguments against extension methods. Also, some tricky questions about overloading handling and compatibility are lurking in the shadow. But if you have good suggestions on how to fix them, I'd take the time to propose something on an OpenJDK mailing list, maybe you get some support.
Can someone explain the rationale for not being able to call a static method on an instance?
A lot of programming language do this, I assume there is a reason, but I don't understand what it is.
Tab completion is much better w/ (extension) methods.
I would pay like, $10,000 of my own money for extension methods like this (actually like Kotlin or Swift) in Typescript. That would solve so many abstraction design problems for me!!
I've used these a lot in C# and it is actually a lot better, and common, to write some extension methods for common operations (e.g. standardized formatting). It's very common to see an "Extensions" folder in a C# project, where these will live.
So what you didn't notice is that you if you need to share SomeFunc across classes, you'd have to put it in a static class, so it's actually SomeStatic.SomeFunc(SomeObject) vs SomeObject.SomeFunc(). So you end up with a load of Utility classes with tons of static methods on them, or you do extensions.
But mostly the advantage is that it's actually clearer when you're reading the code.
It can also add quite a lot to discoverability in the right circumstances, not sure abut Java, but in C# you can include Extensions in global namespaces and so it just turns up in intellisense, without needing to know there's a special utility class.
Visual Studio has actually shipped new functionality recently where it will suggest extension methods (and classes) not included in your usings anyway, and then automatically add the using statements if you choose to use them. I thought this new functionality would annoy me but it's pretty clear from the prompt which objects are already in scope, and I only find myself occasionally having to clean up the using statements.
TL;DR; Looks a trivial change, but it surpisingly improves the readability of code, and the discoverability of home-grown utility functions.
Or, in my opinion, even worse... When you get mix-and-match because you want to extend a fluent API. Using your example, what if `SetWriteTimeout` was a static method you wrote, but all the others were provided by library as a fluent API? Then you end up with this kind of unholy mess:
For me, any functionality that requires a compiler plugin, automatically raises the bar for adopting that functionality. Any extra features have to be contrasted with essentially modifying the language.
Fluent transforms the abstract syntax tree at compile-time resulting in .class'es in a .jar that always work at runtime. There are no runtime dependencies.
Oh shit, you're right. I thought it was java and not javac. My middle night toilet writing on HN failed. :) It makes much more sense because "why would you have a plugin at runtime and not just pass a classpath or jar?" Still, maven and all tools, including language servers, that could compile or interpret it outside of a pom.xml would need that detail or they would break.
This topic makes me think of what other sort of compile-time mutation plugins could be had, including streamlining customizations with a per-project convention. Maybe Erlang bit syntax and matching for Java?
The JDK team won't like this. As it is Fluent, Lombok, and (I presume) Manifold are using all sorts of unsupported hacks to access compiler internals. If you restrict yourself to the compiler plugin API you don't get much mileage.
There are variations, for example where only the last call actually creates, say, a Blob array and where every intermediate call produces something only allowing the next method to be called, so that the IDE autocompletes nearly everything for you and where you won't be able to compile if you miss a single call.
Don't know if the following link is any good but "fluent interfaces" are 15 years old stuff at least by now I'd say:
Hate towards design patterns is a typical sign of not understanding them. Having a builder class to construct a certain subset of generic objects is a normal design practice, so your irony does not look appropriate and does not contribute to the discussion.
var stylesheet = http.get(website.resolve(“style.css”)).asString();
assertThat(stylesheet, contains(“img.jpg”));
Why? Fluent interfaces are good as internal DSL, where semantics of calls are clear and where readability is improved, because it looks closer to the natural language. This means that subject remains the same if you use multiple verbs. Just chaining several imperative statements does not add much value and only complicates debugging.
How would you call a process of determining a correct URL for a request from a base URL and an arbitrary string?
This is a quite common name for it, e.g. https://www.npmjs.com/package/resolve-url
I feel like this misses the reason I like extension methods: discoverability.
With an extension method, I can do `object.` and my IDE will tell me what can be called on object. With a static helper method, it isn't as easy to know what is available. I need to know which helpers actually exist.
Since this doesn't have IDE support, it doesn't help discoverability. I'm not going to get nice autocomplete that shows me what is available. In fact, my IDE is going to highlight it as a bug. If I have a spelling mistake, I won't be able to easily pick it up - I'll assume it's just the normal complaint for all of these fluent extension methods.
That makes this simply syntactic sugar rather than something that actually helps me discover things more easily. It then hurts readability and navigation since I can't easily click through to get the definition of the method.
On a more general note about Java, things like this are one of the reasons I don't love the Java ecosystem. People try to change the behavior of Java in really hacky ways that don't work well. I understand that it's an attempt to overcome shortcomings in the language, but when one looks other languages it becomes clear that Java could have just evolved the language to be better. Java has lots of good things and I'm not looking to argue that. However, when I look at things like this, it makes me think that Java needs to really address the core language.
Instead, we get lots of tools like this which might be nice, but make it really hard to understand what's going on. Electronic Arts created an async/await library that'll do crazy stuff to let you do async/await style programming (https://github.com/electronicarts/ea-async). Yes, Java is doing good things with structured concurrency and Project Loom, but the point is how people keep trying to work around the language. There are so many POJO generators it isn't funny: AutoValue, Immutables, JodaBeans, Lombok, and more I'm probably forgetting. Java records don't fulfill everything (and they're at least a decade late). Java doesn't support expression trees for lambdas so libraries sometimes do crazy hacky things to make that exist.
Java is a great piece of technology, but it feels like people are often trying to overcome issues with the language through really hacky means in a way that I don't see in other languages. Java is getting better about modernizing the language, but it still feels like people are running against the language more than in other ecosystems.
This is "cool", and really demonstrates the extensibility of the language, but definitely a nuclear minefield and something I would never use for anything other than a demonstration.
This really isn't solving any real problems either. The first set of syntax shows definitive programmer intent at the cost of.... 4 extra keyboard presses. The code will also fail compilation or even at runtime if some API changes in a dependency, which is a _good thing_.
If you want extension methods on the JVM, do yourself a favor and pick kotlin.
I know I know, stuff like this exists for when corporate policy insists on sticking with java, but a corporate policy that at the same time rejects kotlin but allows bastardizing java like this it's a broken corporate policy. The good news however is that stuff like this can be an excellent tool to put pressure on that kind of policy ("if we don't get to use the good stuff we'll use the bad stuff!") and if it was made for that use case, I'll wholeheartedly applaude.
That being said, I'd really, really love to see the kotlin treatment applied to other languages, stuff that doesn't have actual kotlin waiting on the sidelines. Once I got used to the ease of left-to-right provided by kotlin context method funnels it's really painful to go back to nested if blocks (even in languages like typescript that apply the type inference of the conditional to the nested block just fine).
This makes me want to try out "ellipsis" arguments. The ellipsis tells the compiler that the intended arguments for a function are the only ones available in scope that agree with the types required for the function, so they can be inferred.
117 comments
[ 2.5 ms ] story [ 180 ms ] thread1. Without annotations you’ll be forced to index all static methods based on the first argument, globally.
2. Method call code completion will be ridiculous. How many static methods have String, File, List, Object(!) etc. as the first parameter? I don’t think anybody wants their code completion to be invaded like that.
The annotations provide intention. Essentially, adding an extension method is not something to be taken lightly, it requires careful consideration, weighing context, frequency, etc.
Fluent will make you a more productive programmer, which may go against corporate policy.
Ha!
Anyway, nice work. Similar to D's uniform function call syntax.
https://tour.dlang.org/tour/en/gems/uniform-function-call-sy...
There's some more use cases in that link too.
[0]: https://www.baeldung.com/scala/implicit-classes
But yeah, implicits are powerful and should be used carefully.
If it's just about ignoring forced exception management, Lombok provides the @SneakyThrows annotation.
I would need something more before I add a new type of extension. Maybe if you could solve the null-check issue.
i.e. rewriting
to In that case I may consider it for my personal projects, and then potentially recommend it at work once it becomes more widespread and has IDE support.https://github.com/manifold-systems/manifold/tree/master/man...
I might want a nearly-Java language that only adds trivial syntax sugar, but are otherwise the same. Groovy sort of had a good initial idea here (being a superset of Java, though again, it has grown apart a bit since).
For me Kotlin lives in this weird limbo between these two extremes. If I want a different language on the JVM I would rather do Scala or Clojure.
assertNotEmpty(getHttpContent(createSharedUrl(website, "styles.css")));
vs
website.createSharedUrl("styles.css").getHttpContent().assertNotEmpty();
Extensions are searchable by syntax and naturally in a namespace, largely diminishing the risk of put it in in a single use function that will be quickly forgotten, and might seem obscure.
The code itself might not be much shorter, but you don't introduce code looking weird, if it's re-usable it's now discoverable, and if your boilerplate contained a lot of micro adjustments on a larger glue code, this glue code is now shorter and way clearer.
This would be very useful. But how are these extension methods more searchable by syntax than the 'old fashioned' static util methods? I admit I haven't tried, but I suspect that code completion in my IDE wouldn't include these extension methods.
Java specific: If you're looking for a static utility method, then `UtilClass.` will also autocomplete. However, what if the function you're looking for isn't in `UtilClass`, but `SomeOtherClass`? What if you don't even know whether a method already exists for what you're trying to accomplish, and it could possibly exist in multiple libraries? Your discoverability engine quickly becomes Google.
The website examples aren't very inspiring indeed. Have a look at some of the extension functions for the Collections interface:
https://github.com/JetBrains/kotlin/blob/master/libraries/st...
In java, this would be a compile time problem, because once the class is compiled the extension method is called with `invokestatic` rather than `invokevirtual` used by class methods.
There is also a healthy fear of change in the java community that could explain it.
This also explains why it took so long for Java to get lambdas. And, generics with primitive types, e.g., ArrayList<int>, via Project Valhalla. It is a super hard problem to upgrade Java without breaking old stuff. Hence, it is a multi-year R&D effort.
I think you can’t look at the past 5 years and the features that are being floated for Java 21 and future releases and think fear of change is the issue—the language is now changing rapidly.
I like his use of the word should, because as we all know it really means won't. I should exercise more, I should stop trolling people on reddit. API designers should control their APIs.
As for the technical objections, I think the conflict-management rules hold the most weight, but this problem is no more difficult than any other in language design. "not overrideable" is a feature, not a bug (can we please give up on behavioural inheritance already?), and discoverability already sucks. I mean, I still use duckduckgo to find javadocs.
I appreciate the intent, but man is this like a minefield waiting to be walked over. Anyone who looks at the code will scratch their head and will not understand what is going on. An annotation would perhaps make it clearer.
Also, how about IDE support? I would argue it is at least as important as the feature itself.
That is the generic argument against extension methods wherever they appear. They are very confusing, until you get used to remembering their existence, then you know what to search for.
Also, how about IDE support? I would argue it is at least as important as the feature itself.
This is mentioned in TFA.
But I agree about "this" being similar concept
The "this" that GP mentions is for for extension methods similar to the article to be regcognizeed as such (never done if the annotation isn't present), they're written such as:
static int Foo( this Bar bar, int biz) => bar.xyz+biz;
Bar bar = new Bar() { xyz=123; };
int result = bar.Foo(456); // Works even if Foo wasn't a part of Bar initially
Elsewhere annotations(called attributes in C# land) can often be used in the same way as in Java to document serialization behaviour,etc. So in practice there aren't much differences apart from how library authors haven't embraced it entirely as much for automagic bindings (partly due to philosophy, partly due to other facilities being more appropriate).
The definition that includes it is so broad that it loses all meaning, it would make "private", "class" or "boolean" also annotations.
I mean it's fine to not like annotations or the decision to use them, but that another language does things differently is almost a tautology and hardly worthy of note. IMO, reasonable people will disagree.
In JavaScript there is .js, .ts, .jsx, and .tsx. I hope that doesn't become a lot more, but for low amounts of variation, it's reasonable.
I think Haskell is a good example of what happens when a language supports too many extensions. Why are there so many? How do people deal with them all? Needing to pick another file extension seems like pressure to do this more sparingly?
"This looks odd to me so no one else can possibly understand it"is not a rigorous argument.
Both Scala and C# have this. I've used it in my own code to clean things up. It's fine.
Using macros you can auto enable them for all types in a project, package, or module.
And opposite to Dart you don't need to define the extension methods in special extension classes but regular utility classes or modules.
Need to see some benchmarks for this, chief.
Still, cool hack, and it's heartening to see that there's enough extensibility to make this possible in 150 lines of code!
If this is just to highlight the merit of static extension methods with a follow up JEP I am all for it, but please don't advertise this as a feature for production. It's a trap.
But tbh, there are some very good arguments against extension methods. Also, some tricky questions about overloading handling and compatibility are lurking in the shadow. But if you have good suggestions on how to fix them, I'd take the time to propose something on an OpenJDK mailing list, maybe you get some support.
I would pay like, $10,000 of my own money for extension methods like this (actually like Kotlin or Swift) in Typescript. That would solve so many abstraction design problems for me!!
So what you didn't notice is that you if you need to share SomeFunc across classes, you'd have to put it in a static class, so it's actually SomeStatic.SomeFunc(SomeObject) vs SomeObject.SomeFunc(). So you end up with a load of Utility classes with tons of static methods on them, or you do extensions.
But mostly the advantage is that it's actually clearer when you're reading the code.
It can also add quite a lot to discoverability in the right circumstances, not sure abut Java, but in C# you can include Extensions in global namespaces and so it just turns up in intellisense, without needing to know there's a special utility class.
Visual Studio has actually shipped new functionality recently where it will suggest extension methods (and classes) not included in your usings anyway, and then automatically add the using statements if you choose to use them. I thought this new functionality would annoy me but it's pretty clear from the prompt which objects are already in scope, and I only find myself occasionally having to clean up the using statements.
TL;DR; Looks a trivial change, but it surpisingly improves the readability of code, and the discoverability of home-grown utility functions.
Better off transforming .java code at compile-time into .class'es in a .jar that always works at runtime.
This topic makes me think of what other sort of compile-time mutation plugins could be had, including streamlining customizations with a per-project convention. Maybe Erlang bit syntax and matching for Java?
https://www.ansys.com/products/fluids/ansys-fluent
As to whether they're worth the addition trouble or not, I sure as heck don't know.
But it's definitely nothing out of the ordinary.
There are variations, for example where only the last call actually creates, say, a Blob array and where every intermediate call produces something only allowing the next method to be called, so that the IDE autocompletes nearly everything for you and where you won't be able to compile if you miss a single call.Don't know if the following link is any good but "fluent interfaces" are 15 years old stuff at least by now I'd say:
https://java-design-patterns.com/patterns/fluentinterface/#e...
What are you passing into http.get? a url, not something resolved.
i appreciate that using an ide makes code navigation easier but i reject the notion that you should need one to navigate the code
With an extension method, I can do `object.` and my IDE will tell me what can be called on object. With a static helper method, it isn't as easy to know what is available. I need to know which helpers actually exist.
Since this doesn't have IDE support, it doesn't help discoverability. I'm not going to get nice autocomplete that shows me what is available. In fact, my IDE is going to highlight it as a bug. If I have a spelling mistake, I won't be able to easily pick it up - I'll assume it's just the normal complaint for all of these fluent extension methods.
That makes this simply syntactic sugar rather than something that actually helps me discover things more easily. It then hurts readability and navigation since I can't easily click through to get the definition of the method.
On a more general note about Java, things like this are one of the reasons I don't love the Java ecosystem. People try to change the behavior of Java in really hacky ways that don't work well. I understand that it's an attempt to overcome shortcomings in the language, but when one looks other languages it becomes clear that Java could have just evolved the language to be better. Java has lots of good things and I'm not looking to argue that. However, when I look at things like this, it makes me think that Java needs to really address the core language.
Instead, we get lots of tools like this which might be nice, but make it really hard to understand what's going on. Electronic Arts created an async/await library that'll do crazy stuff to let you do async/await style programming (https://github.com/electronicarts/ea-async). Yes, Java is doing good things with structured concurrency and Project Loom, but the point is how people keep trying to work around the language. There are so many POJO generators it isn't funny: AutoValue, Immutables, JodaBeans, Lombok, and more I'm probably forgetting. Java records don't fulfill everything (and they're at least a decade late). Java doesn't support expression trees for lambdas so libraries sometimes do crazy hacky things to make that exist.
Java is a great piece of technology, but it feels like people are often trying to overcome issues with the language through really hacky means in a way that I don't see in other languages. Java is getting better about modernizing the language, but it still feels like people are running against the language more than in other ecosystems.
This really isn't solving any real problems either. The first set of syntax shows definitive programmer intent at the cost of.... 4 extra keyboard presses. The code will also fail compilation or even at runtime if some API changes in a dependency, which is a _good thing_.
If you want extension methods on the JVM, do yourself a favor and pick kotlin.
I know I know, stuff like this exists for when corporate policy insists on sticking with java, but a corporate policy that at the same time rejects kotlin but allows bastardizing java like this it's a broken corporate policy. The good news however is that stuff like this can be an excellent tool to put pressure on that kind of policy ("if we don't get to use the good stuff we'll use the bad stuff!") and if it was made for that use case, I'll wholeheartedly applaude.
That being said, I'd really, really love to see the kotlin treatment applied to other languages, stuff that doesn't have actual kotlin waiting on the sidelines. Once I got used to the ease of left-to-right provided by kotlin context method funnels it's really painful to go back to nested if blocks (even in languages like typescript that apply the type inference of the conditional to the nested block just fine).
static URL buildSiteURL() {