Skip to content

Instantly share code, notes, and snippets.

@cardil
Last active May 16, 2024 21:17
Show Gist options
  • Save cardil/b29a81efd64a09585076fe00e3d34de7 to your computer and use it in GitHub Desktop.
Save cardil/b29a81efd64a09585076fe00e3d34de7 to your computer and use it in GitHub Desktop.
[Draft] JEP - Change name of imported type (aliasing)

Summary

Change name of imported type (aliasing)

Goals

The goal is to make code more productive and clean in cases where multiple classes with same name must be used in code. It should relief a developers frustration in that situations.

Non-Goals

None.

Success Metrics

Only metric to be measured here will be developers satisfaction rate on using Java after introducing this feature.

Motivation

Situation that same class exists in code base is common. In most enterprise application there are multiple object models and there is also the code that maps values from one model to the other. Names of those models shouldn't be forged by developers, but business domain names that are used should be utilized. It's directly advised to do that be Domain-driven design (DDD).

Without such a feature developers are pushed to consider renaming a model classes to make them unique, to evade using fully qualified class names.

This feature is present in multiple languages. Most notable in Python, but also strongly present in JVM languages. Scala, Groovy and Kotlin all have this feature implemented. C# is also equipped with this feature.

Groovy

import org.example.view.model.Employee as EmployeeView

Scala

import org.example.view.model.{Employee => EmployeeView};

Kotlin

import org.example.view.model.Employee as EmployeeView

C Sharp

using EmployeeView = org.example.view.model.Employee;

Implementing this feature is asked by multiple developers and it will make Java on par with it's competitors and relief a developers frustration.

There are multiple threads with discussion on this topic. Naming a few:

It's also a topic on various conferences, must notably by Kevlin Henney.

I'm aware that this feature was proposed multiple times in the past (about 20 years ago , JDK-4194542, JDK-4214789) and it was rejected:

This is not an unreasonable request, though hardly essential. The occasional use of fully qualified names is not an undue burden (unless the library really reuses the same simple names right and left, which is bad style).

In any event, it doesn't pass the bar of price/performance for a language change.

I find this reasoning incorrect. As already mentioned, it is desired to have meaningful domain names, without need to suffix them with *Dto, *Entity, or *Data etc. A little bit different models of the same object are also desirable and directly advised by Clean (Hexagonal) architecture:

Typically the data that crosses the boundaries is simple data structures. You can use basic structs or simple Data Transfer objects if you like. Or the data can simply be arguments in function calls. Or you can pack it into a hashmap, or construct it into an object. The important thing is that isolated, simple, data structures are passed across the boundaries. We don’t want to cheat and pass Entities or Database rows. We don’t want the data structures to have any kind of dependency that violates The Dependency Rule.

Additionally as mentioned, situation is different then was in 1999 and Java's competitors, both JVM based and not, has this feature implemented.

Description

This feature should be implemented solely in Java compiler. Compiled code shouldn't be aware that aliasing was used.

package org.example.view;

import org.example.domain.model.Employee;
import org.example.view.model.Employee as EmployeeView;

interface EmployeeMapper {
  EmployeeView map(Employee employee);
}

To implement this changes to compiler should be made in the way that when compiler reads a java class file with lexer tokens should be renamed on the fly. In that way impact of this change will be minimized as further processed will not have access to this alias information as it will be gone at that point.

Some changes are probably required also to javadoc.

TODO: Further detailed description should be written

Alternatives

There are multiple workarounds on this problem, but all of them have their drawbacks:

Giving a unique name using prefix

With this alternative developer should give a unique simple name to all classed that might be in contact with each other, utilizing prefixes or suffixes.

class EmployeeView {
}
class EmployeeModel {
}

This approach is undesirable as names are forced to be concatenated with additional noisy additions. It directly violate naming principle of DDD and defeats usage on packages. If we need unique names, why we need a packages?!

It's also impossible if we don't control both classes. That might be the case when using some kind of RPC mechanism like SOAP, REST, or RMI.

Extend a class

With this approach developers might extend one of the classes, essentially making a wrapper that delegates operations to base class.

package org.example.view;

import org.example.view.model.Employee;

class EmployeeView extends Employee {
  private final Employee delegate;

  @Override
  String fullName() {
    return delegate.fullName();
  }
}

It also requires additional mapping from org.example.view.model.Employee object to EmployeeView wrapper, and extreme amount of error prone boilerplate code in wrapper class. It's also impossible to use when base class uses a final word, as it should . It's also might be impossible to use when using some kind of RPC mechanism.

Testing

Standard unit tests should be enough to properly test this feature.

Risks and Assumptions

There are usual risks of introducing new syntax to the language as exiting tooling must adjust to be able to work well. The risk is mitigated by choosing to limit this change to be solely compile time syntactic sugar.

Dependencies

None.

@ModProg
Copy link

ModProg commented Oct 18, 2023

Something similar would be partial path imports, as they could also elevate some of the similar pain points, and could be even more explicit when read:

package com.example.mapper;

import com.example.dto;
import com.example.entity;

public class Example {
   public dto.Example toDto(entity.Example example) {
      // ...
   }
}

The same already works for inner classes, so I don't see a reason why it wouldn't work here.

@CC007
Copy link

CC007 commented Oct 18, 2023

The same already works for inner classes, so I don't see a reason why it wouldn't work here.

The only issue would be collisions with those inner classes, but if you follow the conventions for class names and package names, that shouldn't be an issue. Also, such collisions could already happen today anyway, so that shouldn't be a showstopper.

@rupe120
Copy link

rupe120 commented Dec 18, 2023

+1

@skybluethis
Copy link

+1

@CYWong-Nick
Copy link

CYWong-Nick commented Dec 30, 2023

+1
This makes me feel Java is lagging behind. Java did not have string template until Java 21, and should we expect import renaming to come in Java 69? Other popular languages (JavaScript, Python, C#) already have this feature, even JVM-based languages like Kotlin, Scala, and Groovy can do it.

Some may say this feature is unnecessary as it is rarely used. However, when "3 billion devices run Java", there is a reasonable chance that 2 or more libraries will use the same class name. You may want to call it a "nice to have" feature since it does not bring new functionality but only makes life easier, in that case please remember why we did not code in C or ASM is because Java is (was?) a nice language to use.

@mouhsineelachbi
Copy link

mouhsineelachbi commented Jan 24, 2024

+1

@DanielLiu1123
Copy link

+1

@gdonoso94
Copy link

+1

@jorgevilaca82
Copy link

+1

@abuepam
Copy link

abuepam commented May 16, 2024

+1 - I am studying JAVa now, and I already think that it would be way better to have than not having :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment