tag:blogger.com,1999:blog-74095974080230817122026-04-17T11:14:54.927+02:00Random Thoughts on Java ProgrammingTrying to define some of my thoughts on Software Design.Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.comBlogger551125tag:blogger.com,1999:blog-7409597408023081712.post-53934951805764448722026-04-11T13:16:00.003+02:002026-04-11T13:16:59.032+02:00Desktop Icons on GNOME<p>GNOME has traditionally always been one to prefer the benefits of a clean Desktop.</p> <p>But I'm not one for traditions.</p> <p>There's an GNOME extention available called Desktop Icons NG (DIN)<sup>1</sup>.</p> <p>What I needed to do was:</p> <ul><li>copy the application file (thingy.desktop) from /usr/share/applications somewhere and then drag it onto my Desktop.</li> <li>Make it Executable</li> <li>Set it to "Allow launching"</li> </ul> <h1>References</h1> <dl><dt>[1] Desktop Icons NG (DING) by rastersoft</dt><dd><a href="https://extensions.gnome.org/extension/2087/desktop-icons-ng-ding/">https://extensions.gnome.org/extension/2087/desktop-icons-ng-ding/</a></dd> </dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-27301275761948085122026-04-08T11:27:00.004+02:002026-04-08T11:27:28.980+02:00VoxxedDays 2026<p>So, I went to VoxxedDays on April 1st and 2nd.<sup>1</sup></p> <p>Some of it was very interesting, and I've retained some notes and links written below.</p> <dl> <dt>Spec-driven development<sup>2</sup></dt> <dd>It seems to be a way to specify the wanted behaviour of your software in such a way that AI can successfully create your software based on your spec. Not entirely convinced of this.</dd> <dt>Lion<sup>3</sup></dt> <dd>Lion is a set of highly performant, accessible and flexible Web Components.</dd> <dt>PicNic<sup>4</sup></dt> <dd><p>PicNic is a online greengrocer/supermarket which is running a Blog on the challenges on scaling up. Quite interesting.</p><p>Some interesting things for example are providing fresh products (bread for instance) within a certain timeframe.</p> <dt>Programming Rules your IDE can tell you about.</dt><dd><a href="https://github.com/jborgers/PMD-jPinpoint-rules">https://github.com/jborgers/PMD-jPinpoint-rules</a></dd> <dt>Compressed OOPs in the JVM</dt><dd><a href="https://www.baeldung.com/jvm-compressed-oops">https://www.baeldung.com/jvm-compressed-oops</a></dd> <dt>A good explanation of generics</dt><dd><a href="https://bramjanssens.nl/generics/">https://bramjanssens.nl/generics/</a></dd> <dt>Reduce Object Header Size and Save Memory in Java 25 </dt><dd><a href="https://www.baeldung.com/java-object-header-reduced-size-save-memory">https://www.baeldung.com/java-object-header-reduced-size-save-memory</a></dd> </dl> <h1>References</h1> <dl><dt>[1] VoxxedDays Amsterdam</dt><dd><a href="https://amsterdam.voxxeddays.com/">https://amsterdam.voxxeddays.com/</a></dd> <dt>[2] What Is Spec-Driven Development? A Complete Guide </dt><dd><a href="https://www.augmentcode.com/guides/what-is-spec-driven-development">https://www.augmentcode.com/guides/what-is-spec-driven-development</a></dd> <dt>[3] Github - Ing/Lion</dt><dd><a href="https://github.com/ing-bank/lion">https://github.com/ing-bank/lion</a></dd> <dt>[4] PicNic - Blog</dt><dd><a href="https://blog.picnic.nl/">https://blog.picnic.nl/</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-53897973545349029482026-03-16T09:36:00.004+01:002026-03-16T09:37:25.107+01:00Kotlin: lots of functions<p>I like Kotlin (sometimes) as it has a large and rich number of convenient functions to use.</p> <p>In Java, we tend to reach for our Guava<sup>1</sup> or Apache Commons Lang<sup>2</sup> to get the same functionality.</p> <p>But it does tend to make it a bit hard to decide which to use, especially as I (as most people I hope) do not know the entire dictionary of available functions in Kotlin.</p> <p>Luckily there's of course colleagues who can help, and my IDE also does a pretty good task of suggesting better ways.</p> <p>Case in point:</p> <div class="code"> val items : List&lt;Items&gt; = getItems()<br/> val coupon: Coupon? = items.map { it.coupon }.filter { it != null }.firstOrNull() </div> <p>I do know the code above can be made simpler.</p> <div class="code"> val items : List&lt;Items&gt; = getItems()<br/> val coupon: Coupon? = items.mapNotNull { it.coupon }.firstOrNull() </div> <p>There, the mapNotNull() is a very convenient function that I use all the time.</p> <p>But this time my IDE complained again and told me to use .firstNotNullOfOrNull</p> <div class="code"> val items : List&lt;Items&gt; = getItems()<br/> val coupon: Coupon? = items.firstNotNullOfOrNull { it.coupon } </div> <p>It's fine by me, to use this, but I didn't know this function exists.</p> <p>Also, the name seems a bit long and it took a minute to understand the meaning.</p> <h1>References</h1> <dl><dt>[1] GitHub - Google Guava</dt><dd><a href="https://github.com/google/guava">https://github.com/google/guava</a></dd> <dt>[2] Maven Repository - Apache Commons Lang (3)</dt><dd><a href="https://mvnrepository.com/artifact/org.apache.commons/commons-lang3">https://mvnrepository.com/artifact/org.apache.commons/commons-lang3</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-68714769781011103972026-02-12T13:46:00.009+01:002026-02-12T13:46:00.121+01:00AssertJ + AutoCloseableSoftAssertions + Kotlin<p>I've been playing around with <tt>AutoCloseableSoftAssertions</tt>, as I don't want to have to remind myself to call <tt>.assertAll()</tt></p> <p>But I also wanted to see if I can replace entire rows of assertThat statements in a simple manner, without many changes.</p> <p>And I think I have found a way in Kotlin.</p> <p>Let's start with the basics:</p> <script src="https://gist.github.com/maartenl/f49cd76716e75317462f7f907a418197.js"></script> <p>Let's try some SoftAssertions, so we can have many asserts that are all evaluated, instead of the Test stopping upon the first failed assert.</p> <script src="https://gist.github.com/maartenl/afe52449181dee3ef6e9134859154ef7.js"></script> <p>Okay, now AutoCloseableSoftAssertions is a convenient class that calls the .assertAll() as part of the final block in a try-with-resources:</p> <script src="https://gist.github.com/maartenl/e3011f752a1020e4d6b747a42e0ec42d.js"></script> <p>Now, in the case above, we have to prefix our assertThat() calls with "it.". There's one more step that we can use to remove this.</p> <script src="https://gist.github.com/maartenl/7e51d656f19056374e15779642917b5a.js"></script> <p>Wrapping all this in a function for easy use will end up in:</p> <script src="https://gist.github.com/maartenl/4596dd12683e64f6fc1056b75dc3135d.js"></script> <p>Now, a stack of <tt>assertThat()</tt> statements can be encompassed with <tt>assertAll{}</tt> without any other changes to get the full advantages of SoftAssertions.</p> <p>Might be overkill, but this was a fun example in Kotlin.</p> <h2>Try-with-resources</h2> <p>So you might have noticed that AutoClosableSoftAssertions is used in a try-with-resources block.</p> <p>The small advantage you have, is that if there is an unexpected Exception thrown during your asserts, the ".assertAll()" will still be called, because it's part of the "AutoCloseable.close()".</p> <p>I don't know how much this advantage is, as the unexpected Exception breaks the test anyways, but there it is.</p> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-13434186583342446962026-02-05T08:15:00.001+01:002026-02-05T08:15:31.113+01:00Retrieving Bookmarks from your Firefox - The Hard Way<p>Yeah, so I turned my old workstation into a server and no longer have access using a graphical interface.</p> <p>Normally, this is fine, as I am using it as a server.</p> <p>But I forgot to export my Firefox bookmarks.</p> <p>Hopefully I can examine the sqllite database using reference [1] to retrieve my bookmarks.</p> <p>I open the file /home/mrbear/.mozilla/firefox/xvosm5y9.default/places.sqlite and selecting the tab "Browse data" found the table moz_bookmarks.</p> <p>It contains all bookmarks and folders and contains references to the moz_places table (using the moz_bookmarks.fk).</p> <p>The moz_places table contains the actual urls.</p> <h1>References</h1> <dl><dt>[1] DB Browser for SQLLite</dt><dd><a href="https://sqlitebrowser.org">https://sqlitebrowser.org</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-13313270809113241372026-01-26T10:09:00.003+01:002026-01-26T10:09:15.430+01:00Git: Cleaning up<p>Sometimes, our IDE can make a mess of things (mostly because we did something wrong, though).</p> <p>And in order to fix it, sometimes we have to get rid of all unversioned files, so that we have a clean git repository and then create a new Project in our IDE.</p> <p>This helps as it forces the IDE to recreate its config files from scratch.</p> <p>This blog is just a small note on how to clean your git repository of all unversioned files.</p> <p>A dry run would look like this:</p> <div class="code">git clean -n -x -d </div> <p>A proper clean would look like this:</p> <div class="code">git clean -d -x </div> <p>Options:</p> <dl><dt>-n</dt><dd>dry run</dd> <dt>-d</dt><dd>also recursively delete unversioned directories.</dd> <dt>-x</dt><dd>ignore .git-ignore rules (convenient for cleaning up buildproducts and IDE config files)</dd> </dl> <p>Output of a dry run would look something like this:</p> <div class="code"> % git clean -n -x -d<br/> Would remove .DS_Store<br/> Would remove .idea/<br/> Would remove mrbear-parent/mrbear-app/target/<br/> Would remove mrbear-stubs/.gradle/<br/> Would remove mrbear-stubs/.kotlin/<br/> Would remove mrbear-stubs/build/<br/> </div> <p>See also "git restore" or "git reset".</p> <h1>References</h1> <dl> <dt>git-clean - Remove untracked files from the working tree</dt><dd><a href="https://git-scm.com/docs/git-clean">https://git-scm.com/docs/git-clean</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-90203927633717553342026-01-15T12:23:00.000+01:002026-01-15T12:23:27.517+01:00Kotlin : NullSafety + Defaults<p>So, I had a discussion with my colleague about null safety and how it can be non-intuitive if you're not yet used to it.</p> <p>So we had the following code:</p> <script src="https://gist.github.com/maartenl/c81bc29432cc9093568a97c08f3bd522.js"></script> <p>In a lot of REST applications, an Exception may be thrown when a resource does not exist. But it's important to differentiate between "no resource" and "oh no! An exception occurred! We're in trouble!".</p> <p>That's what this example code does.</p> <p>So, what did the "getStatus" method do exactly?</p> <script src="https://gist.github.com/maartenl/345a1c084b0cc23a8c421a57ef3a4c43.js"></script> <p>This causes the test to fail.</p> <p>The code should have been "<tt>?: false</tt>", but doing that does look weird.</p> <p>A clearer solution would be:</p> <script src="https://gist.github.com/maartenl/db659f7f0ecd8f249e323f562962586b.js"></script> <p>The problem with Kotlin might be that there's alot of "?." and "?:" and "!!" and quite frankly it makes it hard to read.</p> <p>What do you think?</p>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-86006899436848045412026-01-11T12:58:00.002+01:002026-01-11T12:58:31.808+01:00Trying out the Github Command Line<p>So it's possible and quite convenient to use hardcode git commands to checkout Github repositories. It works fine, once you get the SSH keys sorted out and all that.</p> <p>I tend to use IntelliJ to checkout new github repositories, as it is soo much easier to point and click.</p> <p>This always happens to me, when I find myself having to do things I don't do often. Because once you have a repo checked out, that's basically the end of all the complicated stuff and you just get to work.</p> <h1>Checking out using Git</h1> <p>I still prefer using Git to checkout a new repository for now<sup>4</sup>.</p> <p>The way this works is just do "git clone git@github.com:username/repo.git". You can find this url in the repository on the GitHub website.</p> <p>However, an ssh key must be available on your GitHub account for the computer/client you're using.</p> <p>First generate such an ssh key on your computer, using reference [5].</p> <p>For example: "<tt>ssh-keygen -t ed25519 -C "mrbear@mrbear.com"</tt>".</p> <p>Add your ssh key to the ssh-useragent, using "<tt>ssh-add ~/.ssh/id_ed25519</tt>".</p> <p>You should see:</p> <div class="code">$ ssh-add ~/.ssh/id_ed25519<br/> Enter passphrase for /home/mmrbear/.ssh/id_ed25519: <br/> Identity added: /home/mrbear/.ssh/id_ed25519 (mrbear@mrbear.com)</div> <p>Then you can add the generated key to your Github account using the website, using reference [6].</p> <p>After that, you can finally just clone your repo as mentioned above.</p> <h1>Installation</h1> GitHub has a command line interface nowadays<sup>1</sup>. It's easy to install<sup>2</sup>. <p>I'm using Fedora, so all it takes on my end is to "<tt>sudo dnf install gh</tt>".</p> <p>There used to be a tool called "hub", apparently, which is basically a git wrapper for GitHub. It was an unofficial tool. You can find the differences discusses in [3].</p> <h1>Using the CLI</h1> <p>Logging in using "<tt>gh auth login</tt>".</p> <div class="code">$ gh auth login<br/> ? Where do you use GitHub? GitHub.com<br/> ? What is your preferred protocol for Git operations on this host? SSH<br/> ? Upload your SSH public key to your GitHub account? Skip<br/> ? How would you like to authenticate GitHub CLI? Login with a web browser<br/> <br/> ! First copy your one-time code: DFGF-JFDB<br/> Press Enter to open https://github.com/login/device in your browser... <br/> ✓ Authentication complete.<br/> - gh config set -h github.com git_protocol ssh<br/> ✓ Configured git protocol<br/> ✓ Logged in as mrbear</div> <p>Then the command "<tt>gh issue list</tt>" shows me open issues.</p> <p>Works! Quite nice!</p> <h1>References</h1> <dl><dt>[1] GitHub - Take GitHub to the command line</dt><dd><a href="https://cli.github.com/">https://cli.github.com/</a></dd> <dt>[2] Installing gh on Linux and BSD</dt><dd><a href="https://github.com/cli/cli/blob/trunk/docs/install_linux.md">https://github.com/cli/cli/blob/trunk/docs/install_linux.md</a></dd> <dt>[3] GitHub - GitHub CLI & hub</dt><dd><a href="https://github.com/cli/cli/blob/trunk/docs/gh-vs-hub.md">https://github.com/cli/cli/blob/trunk/docs/gh-vs-hub.md</a></dd> <dt>[4] GitHub - Cloning a repository</dt><dd><a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository">https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository</a></dd> <dt>[5] GitHub - Generating a new SSH key and adding it to the ssh-agent</dt><dd><a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent</a></dd> <dt>[6] GitHub - Adding a new SSH key to your account</dt><dd><a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account">https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-5003413746933172992025-12-28T15:37:00.000+01:002025-12-28T15:37:01.954+01:00NFS - Network File System<p>Network File System (NFS) is used to mount external sources of files between Linux/Unix servers.</p> <p>Nowadays, I think most people use SAMBA (SMB) as that also allows Windows to join the club, but I decided to try NFS (again).</p> <p>I used to work with NFS back in the past, but it seems to still be viable.</p> <h>On the server</h2> <p>We will be sharing the directory <tt>/mnt/raid</tt>.</p> <p>I've edited the file /etc/exports.d/raid.exports to contain<sup>2</sup>:</p> <div class="code">/mnt/raid 192.168.2.0/24(rw,sync,no_root_squash)</div> <dl><dt>rw</dt><dd>read and write access</dd> <dt>sync</dt><dd>is the default, makes certain new requests only get a reply when changes have been committed to stable storage</dd> <dt>no_root_squash</dt><dd>By default root files are re-mapped to nobody, to prevent write-access etc. in my case, I wish to edit files using root. I don't mind the security consequences very much in my home situation</dd> </dl> <p>To apply changes to this file, run "<tt>exportfs -ra</tt>" or restart the NFS server.</p> <p>Start services:</p> <div class="code"># systemctl start nfs-server.service<br/> # systemctl enable nfs-server.service </div> <h2>On the client</h2> <p>To view all mountable directories of a certain server, try this:</p> <div class="code"> # mount | grep raid<br/> watson:/mnt/raid on /mnt/raid type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,fatal_neterrors=none,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.2.1,local_lock=none,addr=192.168.2.6) </div> <p>Or of course try "cat /proc/mounts".</p> <p>Then, on the client, mount the directory:</p> <div class="code"># mount -t nfs -o vers=4 watson:/mnt/raid /mnt/raid</div> <p>Surprisingly, I did not have to actually change any configuration of the NFS service.</p> <p>Works surprisingly well.</p> <h1>References</h1> <dl><dt>[1] Fedora Server User Documentation - File sharing with NFS – Installation</dt><dd><a href="https://docs.fedoraproject.org/en-US/fedora-server/services/filesharing-nfs-installation/">https://docs.fedoraproject.org/en-US/fedora-server/services/filesharing-nfs-installation/</a></dd> <dt>[2] My Blog - My Current MDADM Setup</dt><dd><a href="https://randomthoughtsonjavaprogramming.blogspot.com/2024/09/my-current-mdadm-setup.html">https://randomthoughtsonjavaprogramming.blogspot.com/2024/09/my-current-mdadm-setup.html</a></dd> </dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-40121579616529752742025-12-12T11:21:00.000+01:002025-12-12T11:21:45.998+01:00Git: Deleting old local branches <p>Just looking at old local branches. They tend to proliferate, especially if you have a release cadence.</p> <p>To list the local branches (the default), or list the branches according to a pattern, see the two examples directly below.</p> <div class="code">git branch<br/> git branch --list "V2022*"</div> <p>Small explanation of the output of the command:</p> <ul><li>existing branches are listed</li> <li>the current branch will be highlighted in green and marked with an asterisk</li> <li>any branches checked out in linked worktrees will be highlighted in cyan and marked with a plus sign</li> </ul> <p>I'm usually only interested in local branches, but "-r" shows remote-tracking branches and "-a" shows both remote and local branches, if you're interested.</p> <h1>Removing branches</h1> <div class="code">git branch -D `git branch --list "V2022*"`</div> <p>Bear in mind that branches that are used by a worktree cannot be deleted. Remove the worktree first.</p> <p>Actually, I really like that behaviour.</p> <h1>Checking old branches</h1> <p>Apparently you can sort the list of branches based on last comitterdate.</p> <div class="code">git branch --sort=-committerdate # DESC</div> <p>In the example above, you'll see branches that have been committed to recently at the top.</p> <h1>Finding a commit</h1> <p>This is nice, you can find which branches contain a certain commit quickly.</p> <div class="code">git branch --list --contains 089aafb331a08d19ed805fff6fea3846776980a0</div> <p>Unfortunately, we're currently using git as a local repo, and svn remote, so there's a disconnect between commit hashes.</p> <h1>References</h1> <dl> <dt>Git - git branch documentation</dt><dd><a href="https://git-scm.com/docs/git-branch">https://git-scm.com/docs/git-branch</a></dd> <dt>StackOverflow - Can you delete multiple branches in one command with Git? </dt><dd><a href="https://stackoverflow.com/questions/3670355/can-you-delete-multiple-branches-in-one-command-with-git">https://stackoverflow.com/questions/3670355/can-you-delete-multiple-branches-in-one-command-with-git</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-32236163686454030022025-11-28T09:25:00.002+01:002025-11-28T09:25:12.770+01:00Kotlin: From List To Map<p>So I've been trying to make a Map from a List in Kotlin, and as I have not much experience with Kotlin and not much experience with Maps, I turn to the Internet.</p> <p>Funnily enough, after getting it working, my IDE keeps giving me subtle hints that it can be shorter and more concise.</p> <p>I thought it would be nice to put the steps here.</p> <pre style="font-family:monospace;color: rgb(68, 68, 68); background-color: rgb(243, 243, 243); font-weight: 400; "><span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> primaryKeys = listOf(<span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span>) <span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> firstTry = primaryKeys.map { it to getUser(it) }</pre> <p>In the example above it's not a Map yet, but it's a good first step.</p> <p>As you can tell, it transforms your list into another List of type List&lt;Pair&lt;Long, User&gt;&gt;.</p> <p>Kotlin has a toMap() method that does what we want.</p> <pre style="font-family:monospace;color: rgb(68, 68, 68); background-color: rgb(243, 243, 243); font-weight: 400; "> <span style="color: rgb(31, 113, 153); font-weight: 400;">@Test</span> <span style="color: rgb(68, 68, 68); font-weight: 400;"><span style="color: rgb(68, 68, 68); font-weight: 700;">fun</span> <span style="color: rgb(136, 0, 0); font-weight: 700;">testListToMapConversion</span><span style="color: rgb(68, 68, 68); font-weight: 400;">()</span></span> { <span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> primaryKeys = listOf(<span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span>) <span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> map = primaryKeys.map { it to getUser(it) }.toMap() assertThat(map).isEqualTo( mapOf( <span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Bob"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Jimmy"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Jack"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Henry"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"William"</span>) ) ) }</pre> <p>Now my IDE tells me this can be shortened.</p> <pre style="font-family:monospace;color: rgb(68, 68, 68); background-color: rgb(243, 243, 243); font-weight: 400; "> <span style="color: rgb(31, 113, 153); font-weight: 400;">@Test</span> <span style="color: rgb(68, 68, 68); font-weight: 400;"><span style="color: rgb(68, 68, 68); font-weight: 700;">fun</span> <span style="color: rgb(136, 0, 0); font-weight: 700;">testListToMapConversion</span><span style="color: rgb(68, 68, 68); font-weight: 400;">()</span></span> { <span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> primaryKeys = listOf(<span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span>) <span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> map = primaryKeys.associate { it to getUser(it) } assertThat(map).isEqualTo( mapOf( <span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Bob"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Jimmy"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Jack"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Henry"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"William"</span>) ) ) }</pre> <p>Now my IDE tells me this can be shortened AGAIN!</p> <pre style="font-family:monospace;color: rgb(68, 68, 68); background-color: rgb(243, 243, 243); font-weight: 400; "> <span style="color: rgb(31, 113, 153); font-weight: 400;">@Test</span> <span style="color: rgb(68, 68, 68); font-weight: 400;"><span style="color: rgb(68, 68, 68); font-weight: 700;">fun</span> <span style="color: rgb(136, 0, 0); font-weight: 700;">testListToMapConversion</span><span style="color: rgb(68, 68, 68); font-weight: 400;">()</span></span> { <span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> primaryKeys = listOf(<span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span>) <span style="color: rgb(68, 68, 68); font-weight: 700;">val</span> map = primaryKeys.associateWith { getUser(it) } assertThat(map).isEqualTo( mapOf( <span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">12L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Bob"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">15L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Jimmy"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">16L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Jack"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">22L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"Henry"</span>), <span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span> to User(<span style="color: rgb(136, 0, 0); font-weight: 400;">204L</span>, <span style="color: rgb(136, 0, 0); font-weight: 400;">"William"</span>) ) ) }</pre> <p>Nice!</p> <h1>References</h1> <dl><dt>Syntax Highlighter</dt><dd><a href="https://highlight.hohli.com/">https://highlight.hohli.com/</a></dd> </dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-65817267857821880832025-11-20T11:42:00.001+01:002025-11-20T11:42:00.115+01:00Kotlin: Redundant SAM constructor<p>Kotlin lambdas are fully compatible with Java functional interfaces. </p> <p>But sometimes you need to give Kotlin a little nudge, using a SAM constructor.</p> <p>SAM constructors (Single Abstract Method) allow you to convert a lambda expression to an instance of a functional interface. The syntax is pretty straightforward.</p> <div class="code">FunctionalInterfaceName { lambda_function } </div> <p>The message in the title appears when you use a SAM constructor, when you don't have to. Kotlin is smart enough to create the appropriate anonymous class without us being specific in most cases.</p> <p>I notice this happening sometimes when I have IntelliJ convert my Java class automatically to Kotlin.</p> <p>A simple example:</p> <div class="code"> // Java<br/> public Builder addMapping(FieldMetadata field, Supplier&lt;?&gt; valueSupplier) {...} </div> <div class="code"> // redundant Kotlin<br/> val fieldMapping = FieldMapping.builder()<br/> .addMapping(OrderItemField.ITEM_NR), Supplier { orderRepository.getOrderItem })<br/> .build()</div> <div class="code"> // correct Kotlin<br/> val fieldMapping = FieldMapping.builder()<br/> .addMapping(OrderItemField.ITEM_NR) { orderRepository.getOrderItem }<br/> .build()</div> <h1>References</h1> <dl><dt>Medium - Idiomatic Kotlin: Lambdas and SAM constructors </dt><dd><a href="https://medium.com/tompee/idiomatic-kotlin-lambdas-and-sam-constructors-fe2075965bfb">https://medium.com/tompee/idiomatic-kotlin-lambdas-and-sam-constructors-fe2075965bfb</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-6393003753327951672025-11-13T13:59:00.001+01:002025-11-13T13:59:00.121+01:00Rename .java to .kt<p>So, I've suddenly recently noticed that whenever I commit a change into Git in IntelliJ that contains a conversion of a .java file into a .kt (Kotlin) file, IntelliJ will automatically make a <b>previous</b> commit containing the comment "Rename .java to .kt" which contains ONLY the renaming of the file.</p> <p>I thought this was odd, but the reason behind it is that this commit helps Git to bind the two files together in the History.</p> <p>If you do not have this single commit, (for example, if you're merging this to your integration branch or whatever and you squash your commits), you lose the history. It means Git will see the .java file as a file that has been deleted and the .kt file as a new file that has been added.</p> <p>Some people complain, but it really depends on what is important to you: <ul><li>do you want to preserve your history in Git for a file<br/></li>or<li>do you want to see the changing the filename as belonging to your commit (and your ticketnumber in de comments)</li></ul> <p>Ideally, you should bear in mind IntelliJ does this, so you can at least edit the Commit Message of the renaming to include your ticketnr and original comment and such.</p> <h1>Settings</h1> <p>Can you turn this setting off? Yes, you can. There's a checkbox in the settings of the Git Commit dialog.</p> <p>Unfortunately, this interesting setting only appears when you have indeed converted a Java file into a Kotlin file.</p> <div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMlv83pdtxwdk9U4UDZOrKf4FB4LKcwziiCKlGVBHIorfgzoGPExMCMzQLa4BahPchWr8hfQ_tGWywm_LGXHD0xdxZM5eWJnis9HRTc49VcQfmTrQkBfUNcacTgC8yz48OtNQqrSYti6rJDt_Do38sxFVa5Al7iKgDoX7toRgXdLahvLdxFNtysDdygT1i/s1600/Screenshot%202025-11-04%20at%2014.12.32.png" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" data-original-height="725" data-original-width="412" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMlv83pdtxwdk9U4UDZOrKf4FB4LKcwziiCKlGVBHIorfgzoGPExMCMzQLa4BahPchWr8hfQ_tGWywm_LGXHD0xdxZM5eWJnis9HRTc49VcQfmTrQkBfUNcacTgC8yz48OtNQqrSYti6rJDt_Do38sxFVa5Al7iKgDoX7toRgXdLahvLdxFNtysDdygT1i/s1600/Screenshot%202025-11-04%20at%2014.12.32.png"/></a></div> <h1>References</h1> <dl> <dt>Kotlinlang - slack-chats</dt><dd><a href="https://slack-chats.kotlinlang.org/t/465094/hi-i-ve-discovered-to-my-surprise-that-the-java-to-kotlin-co">https://slack-chats.kotlinlang.org/t/465094/hi-i-ve-discovered-to-my-surprise-that-the-java-to-kotlin-co</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-14223839016421090492025-11-03T09:22:00.002+01:002025-11-03T09:22:56.914+01:00JFall 2025<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbd31zvnSeNzVdFcMYOBc2RlFDQORrRoO5PDwL76YG_we4WdO8bMcHAKpldD0PhyphenhyphenBNWjkEQ707ox0Hi8t9RvO2DuxnxgrSS4g8bpr4SO7gSabB9tgZI-01PzoFRPwMfVSFamw5Nef0djcQ/s1600/-var-www-org.nljug-current-application-assets-media-uploads-2015-06-17-jfall2015_v41.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbd31zvnSeNzVdFcMYOBc2RlFDQORrRoO5PDwL76YG_we4WdO8bMcHAKpldD0PhyphenhyphenBNWjkEQ707ox0Hi8t9RvO2DuxnxgrSS4g8bpr4SO7gSabB9tgZI-01PzoFRPwMfVSFamw5Nef0djcQ/s320/-var-www-org.nljug-current-application-assets-media-uploads-2015-06-17-jfall2015_v41.jpg" width="146" /></a></div> <p>So last year I was unable to get tickets. They sold out pretty quickly (like in the first three minutes, I hear?).</p> <p>I expected not to be able to attend this year either, but I added myself to the waiting list and hoped for the best.</p> <p>Apparently, I was in luck! I have tickets!</p> <p>I am planning on attending the following:</p> <ul> <li>Catching the 137-Killer: A Java Memory Forensics Investigation <br/>Martijn Dashorst</li> <li>Java; our personal career companion<br/>Peter Schuler Ragna Gerretsen</li> <li>Why You Should Build Agents on the JVM<br/>Rod Johnson</li> <li>Java 25 - Better Language, Better APIs, Better Runtime<br/>Nicolai Parlog</li> <li>xz: The day the internet (almost) died<br/>Reinier Zwitserloot Roel Spilke</li> <li>curl | bash | hacked: The Unseen Dangers in Your Dev Lifecycle <br/>Steve Poole</li> <li>Benchmarking Project Valhalla<br/>Cay Horstmann</li> <li>The Wait is Over: Foreign Function & Memory (FFM) API brings modern Java to the Raspberry Pi<br/>Frank Delporte</li> </ul> <p>However, there's several that I also would have liked to see. I'll await those on the YouTubes.</p> <p>It'll take place coming Thursday, 6th of November 2025.</p> <h1>References</h1> <dl><dt>NLJUG - JFall</dt><dd><a href="http://jfall.nl/">http://jfall.nl/</a></dd></dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-74278796649424378632025-10-23T09:33:00.001+02:002025-10-23T09:33:00.121+02:00No "new" keyword in Kotlin<p>So I was wondering what my opinion is about that.</p> <p>I don't like it a lot, as now it seems like calling a constructor looks similar to calling a method.</p> <p>The only difference that's visible is that the constructor begins with a capital, and then only if you properly follow the coding style guidelines.</p> <p>I noticed that where Java prefers clarity of purpose, Kotlin prefers brevity (and sacrifices clarity for this).</p> <p>In Java the "new" keyword does a lot of heavy lifting, that is not part of the constructor. The constructor merely sets the internal structure of an object-to-be to appropriate values. The responsibility of actually making the object, registering it in de Heap, doing the pointer bits, is indicated by the new keyword.</p> <p>What are your opinions?</p> <h1>References</h1> <dl><dt>Reddit - Is keyword new redundant? </dt><dd><a href="https://www.reddit.com/r/java/comments/1n0m7cg/is_keyword_new_redundant/">https://www.reddit.com/r/java/comments/1n0m7cg/is_keyword_new_redundant/</a></dd> <dt>Kotlin Documentation - Classes</dt><dd><a href="https://kotlinlang.org/docs/classes.html">https://kotlinlang.org/docs/classes.html</a></dd> </dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-47766356233238846042025-10-16T16:33:00.007+02:002025-10-16T16:33:00.152+02:00Bridge Methods in Java<p>In Java, Generics are erased ("type erasure") at compile time. This concept introduces a problem where an abstract super class containing generics &lt;T&gt; will be compiled to abstract super class with "Object".</p> <p>In which case implementation of this abstract class with specific Generics (say for example &lt;String&gt;) will cause the compiler to create Bridge Methods, in this case the someMethod(Object o) will call someMethod(String o) with an appropriate cast.</p> <p>Reference [1] and [2] has a much better explanation</p> <p>You won't find it in the JLS, as it is an implementation detail. But interesting none the less.</p> <h1>References</h1> <dl> <dt>[1] Medium - Bridge Methods in Java </dt><dd><a href="https://medium.com/@rohitsingh341/bridge-methods-in-java-21a9d06b6b1b">https://medium.com/@rohitsingh341/bridge-methods-in-java-21a9d06b6b1b</a></dd> <dt>[2] The Java Tutorials - Effects of Type Erasure and Bridge Methods </dt><dd><a href="https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html">https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html</a></dd> </dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-31393487105689893742025-10-06T10:25:00.004+02:002025-10-09T12:24:05.259+02:00Optional.or<p>Was working on something, and I wanted to combine two optionals. In my case, I know that only one of the two will be present (or none), so combining them would be nice.</p> <p>I'm not a big fan of using .ifPresent() and .get() combos. So, let's try streams.</p> <p>Using streams, this looks something like:</p> <script src="https://gist.github.com/maartenl/a3d38705ea2427676469455222fbbeaf.js"></script> <p>Luckily we have a Optional.or() nowadays (Since Java 9), that I haven't used before.</p> <p>It looks a lot better:</p> <script src="https://gist.github.com/maartenl/14275fe15e871998b29081a53f1f58bc.js"></script> <p>The awesome part (which is not shown in the example above) is that the or() accepts a Supplier, which means the Supplier will not be called if a value is present in the first Optional.</p> <p>This is similar to the short-circuit evaluation<sup>1</sup> present in the || operator of the common if-statement.</p> <h1>References</h1> <dl> <dt>[1] Wikipedia - Short-circuit evaluation </dt><dd><a href="https://en.wikipedia.org/wiki/Short-circuit_evaluation">https://en.wikipedia.org/wiki/Short-circuit_evaluation</a></dd> <dt>[2] Baeldung - Guide To Java Optional </dt><dd><a href="https://www.baeldung.com/java-optional">https://www.baeldung.com/java-optional</a></dd> <dt>[3] Combine two Java Optionals </dt><dd><a href="https://www.agalera.eu/combine-two-optionals/">https://www.agalera.eu/combine-two-optionals/</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-25135791557753914972025-08-29T09:26:00.005+02:002025-08-29T11:46:09.162+02:00Growing the Java Language #JVMLS<p>Just a blorb to remind me of the presentation by Brian Goetz during 2025 JVM Language Summit.<p> <p>which was made available on the YouTube<sup>1</sup>.</p> <iframe width="480" height="270" src="https://youtube.com/embed/Gz7Or9C0TpM?si=ejyjWLRKwSY05AKW" frameborder="0"></iframe> <p>P.S. in the link above was a reference to a presentation (in written form) by Guy Steel<sup>2</sup>, that was insightful.</p> <h1>References</h1> <dl>[1] YouTube - Growing the Java Language #JVMLS<dt></dt><dd><a href="https://youtu.be/Gz7Or9C0TpM?si=ejyjWLRKwSY05AKW">https://youtu.be/Gz7Or9C0TpM?si=ejyjWLRKwSY05AKW</a></dd> <dt>[2] Growing a Language - Guy L. Steel Jr.</dt><dd><a href="https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf">https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-54103413357398582802025-08-07T08:27:00.001+02:002025-08-07T08:27:00.118+02:00Converting images to pdf in Linux<p>Occasionally, I have to scan official papers (dead-tree-copy) and I'd like to keep the result small, and in one pdf.</p> <p>So, a one liner command would be nice.</p> <div class="code">magick mrbear*.png -quality 10% -set filename:base "%[basename]" "%[filename:base].jpg"</div> <p>The commandline above automatically creates an appropriate .jpg file for each mrbear*.png file. So mrbear_0001.png will cause a mrbear_0001.jpg.</p> <p>Just what I wanted. Went from 8 megabytes picture to a 200 kb picture.</p> <p>And then converting the new images over to a pdf file is ridiculously simple:</p> <div class="code">magick mrbear*.jpg mrbear.pdf</div> <p>Magick!</p> <h1>References</h1> <dl><dt>DigitalOcean - Reduce File Size of Images in Linux - CLI and GUI methods </dt><dd><a href="https://www.digitalocean.com/community/tutorials/reduce-file-size-of-images-linux">https://www.digitalocean.com/community/tutorials/reduce-file-size-of-images-linux</a></dd> <dt>GitHub - Convert images but keep the original file name #6494 </dt><dd><a href="https://github.com/ImageMagick/ImageMagick/discussions/6494">https://github.com/ImageMagick/ImageMagick/discussions/6494</a></dd> <dt>ImageMagick</dt><dd><a href="https://imagemagick.org/">https://imagemagick.org/</a></dd> </dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-26388540884443976842025-07-31T13:32:00.001+02:002025-07-31T13:32:00.120+02:00LocalSend<p>I've always had problems getting files transferred from tablets, mobile phones, photocamera, MacBooks, Linux, Windows.</p> <p>It often boils down to having to install an app of somekind, or mounting a virtual filesystem (if we're talking about Linux) or installing a driver. And as I do not do it often, I always forget which tool I used and what steps I had to take.</p> <p><b>Well, not anymore!</b></p> <p>Thank the heavens for <b>LocalSend</b><sup>1</sup>. It does everything I need, in a very easy way and it's Open Source too!</p> <p>It's just a small network app that uses a REST Service and HTTPS to transfer files to other instances of itself on the Local Network. No internet required.</p> <h3>Addendum</h3> <p>The only thing to keep in mind, is that some devices (notably my MacBook) will ask for permission from you before allowing LocalSend (or any other Network App) to actually access the Local Network.</p> <h1>References</h1> <dl> <dt>[1] LocalSend: Share files to nearby devices</dt><dd><a href="https://localsend.org/#/">https://localsend.org/#/</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-65787486434194305582025-07-22T09:30:00.000+02:002025-07-22T09:30:16.726+02:00John Cleese Quote on Creativity<blockquote><i>“Nothing will stop you being creative so effectively as the fear of making a mistake. To play is to experiment. What happens if I do this? What would happen if I do that? What if. The very essence of playfulness is an openness to anything that may happen. The feeling that whatever happens it's okay. So you cannot be playful if you're frightened that moving in some direction will be wrong. Something you shouldn't have done. You are either free to play or you're not. So you gotta risk saying things that are silly, and illogical and wrong and the best way to get the confidence to do that is to know while you're being creative nothing is wrong. There's no such thing as a mistake and any dribbel may lead to a breakthrough.”</i></blockquote>- John Cleese<br/><br/> <p>I find that the quote above also applies to Software Design.</p> <p>In that regard, having Technical Debt is a great way to slowdown or downright stop creativity.</p> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-8769022147993748892025-07-10T13:40:00.001+02:002025-07-10T13:40:00.131+02:00@Nonnull versus @NotNull<p>A quick small blog...</p> <p>So, we have two (seemingly) conflicting annotations for the same thing<sup>1</sup>.</p> <p>Even people who know exactly when to use what have a chance to pick the wrong one, especially with the use of Code Completion and AI nowadays.</p> <p>So here's a quick rundown, but the reference below is much more in depth:</p> <dl> <dt><div class="code">import javax.annotation.Nonnull;<br/><br/>@Nonnull</div></dt> <dd>IDEs and Compilers use it for Static Analysis of code. (Though the RetentionPolicy is set to RUNTIME)</dd> <dt><div class="code">import jakarta.validation.constraints.NotNull;<br/><br/>@NotNull</div></dt> <dd>Validation Frameworks (like ORMs) use it during Runtime for checks.</dd></dl> <h2>What about Nullable values?</h2> <dl> <dt><div class="code">import javax.annotation.Nullable;<br/><br/>@Nullable</div></dt> <dd>IDEs and Compilers use it for Static Analysis of code.</dd></dl> <p>There is no @Nullable available for jakarta.validation.constraints. By default things are nullable, unless explicitly mentioned otherwise.</p> <h2>When not to use @NotNull</h2> <p>The primary really important thing to take away from this is to not do the following:</p> <script src="https://gist.github.com/maartenl/5b642b00209c2d9b48f5e060c61c27c0.js"></script> <p>It might work, your IDE might complain or it might not, but at runtime this will be ignored, unless this method is called by a Framework as mentioned above.</p> <h2>Addendum</h2> <p>Don't use javax.validation.constraints.NotNull. It's been superseded by Jakarta when Oracle moved Java EE to Jakarta. See [2].</p> <h1>Referenties</h1> <dl> <dt>[1] Medium - Understanding @NotNull vs @Nonnull in Java: A Practical Guide to Null-Safety </dt><dd><a href="https://medium.com/@mesfandiari77/understanding-notnull-vs-nonnull-in-java-a-practical-guide-to-null-safety-5e179e918f37">https://medium.com/@mesfandiari77/understanding-notnull-vs-nonnull-in-java-a-practical-guide-to-null-safety-5e179e918f37</a></dd> <dt>[2] Medium - Difference between javax.validation and jakarta.validation </dt><dd><a href="https://medium.com/@ovinduuu/difference-between-javax-validation-and-jakarta-validation-df0c6dbe5cb3">https://medium.com/@ovinduuu/difference-between-javax-validation-and-jakarta-validation-df0c6dbe5cb3</a></dd> </dl> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-1906466513248146382025-06-27T10:01:00.001+02:002025-06-27T10:01:00.116+02:00Brain fart<p>Sometimes, when I'm programming, I haven't got a good idea of how to do something. What I do have is a large collection of really bad ideas, that I use when I don't have a good idea.</p> <p>Apparently, good ideas take time. And sometimes they only happen after trying out some really horrendous ideas.</p> <p>So, I checked the follwing String expression in Kotlin in, and I got some code review comments on it.</p> <div class="code">"$description $additionalDescription"</div> <p>The comment was: "put a trim() on the entire thing, as the additionalDescription, which is provided by the user, can be anything (like for example spaces or just empty)".</p> <p>And I got stuck on that. I came up with something horrendous like the following:</p> <div class="code">"${(description + additionalDescription).trim()}"</div> <p>Of course you can immediately see where I went wrong, but once you get invested in the String template solution, sometimes it's hard to break out of that solution again.</p> <p>The solution I went for was (obviously):</p> <div class="code">"$description $additionalDescription".trim()</div> Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-79881714542271777992025-06-21T08:05:00.006+02:002025-06-21T08:05:42.396+02:00Git Large File Storage (LFS)<p>One of my hobby projects is using big files. Github complains about it:</p> <div class="code"> remote: warning: File mrbear/Terrain_PureNature.asset is 58.17 MB; this is larger than GitHub's recommended maximum file size of 50.00 MB <br/> remote: warning: GH001: Large files detected. You may want to try Git Large File Storage<sup>1</sup> - https://git-lfs.github.com. </div> <p>Luckily this is only a warning. The real error happens when trying to push 2GB or bigger files onto the github<sup>2</sup>.</p> <p>So, after installing it on my system...</p> <div class="code">brew install git-lfs</div> <p>And telling git I wish to use it:</p> <div class="code">git lfs install</div> <p>I had to tell it which files were the primary culprits (apparently we do not want ALL files to use LFS).</p> <div class="code">git lfs track ".png"<br/> git lfs track ".asset"<br/> git lfs track ".fbx"<br/> git lfs track ".unity" </div> <p>Let's not forget to add those new settings to the repo.</p> <div class="code">git add Assets/.gitattributes</div> <p>Apparently this will only work on new files. Existing files in the repo are not changed.</p> <p>But your could try the command git-lfs-migrate (which will change your repos history) for existing files<sup>3</sup>.</p> <h2>Addendum</h2> <p>I think it can be argued that, if you need to use LFS a lot for your git repo, git might actually not be the right tool for you.</p> <p>But I'll leave that argument to smarter people.</p> <h1>References</h1> <dl><dt>[1] Git Large File Storage (LFS)</dt><dd><a href="https://git-lfs.github.com">https://git-lfs.github.com</a></dd> <dt>[2] GitHub - About Git Large File Storage</dt><dd><a href="https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-git-large-file-storage">https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-git-large-file-storage</a></dd> <dt>[3] Github Blog - Git LFS 2.2.0 released</dt><dd><a href="https://github.blog/open-source/git/git-lfs-2-2-0-released/">https://github.blog/open-source/git/git-lfs-2-2-0-released/</a></dd> </dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0tag:blogger.com,1999:blog-7409597408023081712.post-3801600318479897622025-05-29T16:38:00.003+02:002025-05-29T16:38:00.228+02:00JPA Entity Lifecycle Events<p>Apparently JPA Entity have a lifectcycle. See [1].</p> <h1>References</h1> <dl> <dt>[1] Baeldung - JPA Entity Lifecycle Events </dt><dd><a href="https://www.baeldung.com/jpa-entity-lifecycle-events">https://www.baeldung.com/jpa-entity-lifecycle-events</a></dd></dl>Turbohttp://www.blogger.com/profile/05959847299811800007noreply@blogger.com0