You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
add flexible types to deal with Java-defined signatures under -Yexplicit-nulls (#18112)
This is a continuation of #17369.
When dealing with reference types from Java, it's essential to address
the implicit nullability of these types. The most accurate way to
represent them in Scala is to use nullable types, though working with
lots of nullable types
directly can be annoying. To streamline interactions with Java
libraries, we introduce the concept of flexible types.
The flexible type, denoted by `T?`, functions as an abstract type with
unique bounds: `T | Null ... T`, ensuring that `T | Null <: T? <: T`.
The subtyping rule treats a reference type coming from Java as either
nullable or non-nullable depending on the context. This concept draws
inspiration from Kotlin's [platform
types](https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types).
By relaxing null checks for such types, Scala aligns its safety
guarantees with those of Java. Notably, flexible types are
non-denotable, meaning users cannot explicitly write them in the code;
only the compiler can construct or infer these types.
Consequently, a value with a flexible type can serve as both a nullable
and non-nullable value. Additionally, both nullable and non-nullable
values can be passed as parameters with flexible types during function
calls. Invoking the member functions of a flexible type is allowed, but
it can trigger a `NullPointerException` if the value is indeed `null`
during runtime.
```scala
// Considering class J is from Java
class J {
// Translates to def f(s: String?): Unit
public void f(String s) {
}
// Translates to def g(): String?
public String g() {
return "";
}
}
// Use J in Scala
def useJ(j: J) =
val x1: String = ""
val x2: String | Null = null
j.f(x1) // Passing String to String?
j.f(x2) // Passing String | Null to String?
j.f(null) // Passing Null to String?
// Assign String? to String
val y1: String = j.g()
// Assign String? to String | Null
val y2: String | Null = j.g()
// Calling member functions on flexible types
j.g().trim().length()
```
valYexplicitNulls:Setting[Boolean] =BooleanSetting(ForkSetting, "Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.")
420
+
valYnoFlexibleTypes:Setting[Boolean] =BooleanSetting(ForkSetting, "Yno-flexible-types", "Disable turning nullable Java return types and parameter types into flexible types, which behave like abstract types with a nullable lower bound and non-nullable upper bound.")
420
421
valYcheckInit:Setting[Boolean] =BooleanSetting(ForkSetting, "Ysafe-init", "Ensure safe initialization of objects.")
421
422
valYcheckInitGlobal:Setting[Boolean] =BooleanSetting(ForkSetting, "Ysafe-init-global", "Check safe initialization of global objects.")
422
423
valYrequireTargetName:Setting[Boolean] =BooleanSetting(ForkSetting, "Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation.")
@tu lazyvalClassCastExceptionClass_stringConstructor:TermSymbol=ClassCastExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
@tu lazyvalArithmeticExceptionClass_stringConstructor:TermSymbol=ArithmeticExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
0 commit comments