First of all, I know that officially, the answer to this question is that it simply cannot be done in a native Java environment. So I'm not asking if this is something that Java can do natively. I'm simply asking if it can be done in Java AT ALL - perhaps by some other means that I am not aware of.
I've been experimenting with a library that I want to have where a developer can interact with an object where that object can be identified by not making a reference to it, where I then simply refer to it in the library by a default name, OR, the developer can assign names to the object in which case, each new name given will create a new instance of the object in the library.
But in terms of providing the public methods that are relevant to what the library does, I've been having to create TWO of every method and this has significantly increased the size of the library where so much of it has to be duplicated because of the way Java does method overloading. And yes, I do consolidate repeating code everywhere I can but it doesn't change the fact that I have to create two methods for each offered function.
Ideally, this
public static void getThis(float value, String objectName) {
//related code for objectName
}
public static void getThis(float value) {
//related code for the defaultObjectName
}
Becomes this if I could assign a default value for objectName
public static void getThis(float value, String objectName = defaultObjectName) {
//related code handles objectName
}
So my question is, can this be done by ANY means at all - third-party options maybe?
Also, does anyone know why Java does not offer this ability?
edit: I deleted a portion of this last question after reading the first comment.
CodePudding user response:
Also, does anyone know why Java does not offer this ability while so many other languages (C for example) do? Is it some kind of law violation in the space of rigid OOP standards?
No strongly held beliefs, no. It's more complicated than you might think and the team is working on other stuff; it won't be there in one of the next 2 to 3 versions, but maybe later (note that one version comes out every 6 months).
So my question is, can this be done by ANY means at all - third-party options maybe?
Builder pattern or lombok-style annotation processor seem relevant. We (from Project Lombok) haven't added it yet because, well, its really complicated. What if your default is System.currentTimeMillis()
, that should probably be calculated 'live', every time you invoke the method. But what if the default is 'someComplexConstant', should that be pointlessly re-calculated every time? I'm sure you have a preference but that's the exact problem: Some want A, some want B, and neither camp has a 90% market share, which means you have a gigantic chunk of programmers who get a nasty surprise (example: Python's default args are evalled only once. 're-eval every time' is on its face more sensible but folks always bring baggage from other languages they know, and thus trivially you get to "neither camp has an overwhelming majority", which is, at least for us, a showstopper).
Possibly the solution is to simply not allow anything that isn't compile-time-constant, but then you bother users with the definition of CTCs which aren't as clear cut as one might think (did you know null
isn't one, for example? Maybe we should allow all CTCs and also null
... but something like LocalDate.of(1900, 1, 1)
also isn't one and that seems like quite a big annoyance!).
The cost of building this on your own is highly unlikely to be worth your while. If you really want, you could make a 'template' class that is never used by anything, except an annotation processor, which then makes the actual source file you really want. However, going down this road has significant dev process downsides: Your code is just broken until you run a full compile build with annotation processors on, which takes time, and therefore slows your dev cycle way down. Anytime you mess with the 'template' you are likely going to end up with 'everything is totally broken until we do a full clean and rebuild cycle', and if you're doing that, you're doing it wrong. You want to see the effects of anything you write to be visible in at most 2 seconds - hot code replace, incremental compilation, and fast boots are crucial (and you get HCR in eclipse for free, and IC in just about every IDE).
Lombok gets around this by directly patching your IDE to handle the annotations as-you-type and incrementally, but as I mentioned, this is a major feature that is tricky to implement, and right now lombok sticks to the rule that simple annotation processing must be able to do the job, and that means the input source file must be syntactically correct. void foo(int x = 5)
isn't, and the alternatives suck (void foo(@DefaultValueInt(5) x)
? Meh).
A third option is to just forego that idea of syntactically valid java and patch everything. But at some point you have to admit you're just writing software that will take over all systems on boot and hacks/patches all relevant tools to actually parse/support a language that isn't java anymore.
We could try to support LocalDate.of
and hardcode a big list of 'known' constant values, or allow hackery where you set as default value a static final field, which you can then define however you like. This will all work, but the point of a nice feature is presumably that it makes sense to all. If you need to read 5 pages of documentation and re-read it after a month because the actual way it works just doesn't feel logical to you - that's a real problem.
Also, the effort needed to write lombok or something like it, let alone maintain it, is quite significant.
SOURCE: I've been a core lombok dev for about 12 years now :)
CodePudding user response:
You could consider a structure like this:
interface ContextFactory {
Context getDefaultContext();
Context getExplicitContext(String objectName);
}
interface Context {
void getThis(float value);
}
Incidentally you are not doing users of your library any favours by giving them static methods. How will they unit test their code?
Suppose I write:
...
void doIt() {
if (Library.getThis(10) > 0) {
service.effectSomething();
} else {
service.effectSomethingElse();
}
}
...
and I want to write a unit test for that function.
I somehow have to set things up so that in one of my test cases Library.getThis(10)
returns greater than zero, and in another test case it returns zero or less.
If I have an interface I can mock which has been injected into my class, then it's easy. If I'm calling static methods then it's hard.