Pakete, Sichtbarkeit & Exceptions
Paket- und Import-Regeln, Sichtbarkeitsmodifikatoren (public als Default, plus internal), Top-Level-Deklarationen - und das Große für Java-Entwickler: Kotlin hat keine Checked Exceptions.
Deutsche Übersetzung in Arbeit
Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.
Auf dieser Seite
A few structural rules round out Kotlin's everyday syntax: how packages and imports work, the visibility
modifiers (with a different default and a genuinely new one, internal), and the change most likely to surprise
a Java developer - Kotlin has no checked exceptions. None of these are hard, but each differs from Java in a
way worth knowing before you write real code.
Packages and imports
Packages work much like Java's, with one relaxation: a Kotlin file's package doesn't have to mirror its directory, and a single file can hold many top-level declarations (classes, functions, properties) rather than one public class per file:
package com.ledger.billing // declared at the top; directory need not match (though matching is good practice)
import com.ledger.core.Money // import a class
import com.ledger.util.formatMoney // import a top-level function directly
import com.ledger.util.* // wildcard import
import java.util.Date as JavaDate // import alias (resolve name clashes)Two conveniences: you can import functions and properties directly (not just classes, since Kotlin has
top-level ones), and import aliases (as) let you rename an import to resolve a clash - handy when mixing,
say, java.util.Date and a Date of your own.
Visibility modifiers
Kotlin has four visibility modifiers, and the default differs from Java:
public- visible everywhere. This is the default (Java's default is package-private), so an undecorated declaration is public.private- visible only within the file (for top-level declarations) or within the class (for members).protected- visible in the class and its subclasses (not the package - unlike Java, there's no package-private-plus-subclass).internal- the new one: visible everywhere in the same module (a compiled unit - a Gradle source set). Nothing outside the module can see it.
internal class BillingEngine { ... } // usable across this module, invisible to other modules
private fun helper() { ... } // only within this fileinternal fills a real gap: it's how you expose something to your whole module while keeping it out of the
public API other modules consume - exactly the module-boundary discipline from the modular-monolith lesson,
supported at the language level. Note there is no package-private in Kotlin; internal (module-wide) and
private (file/class) cover those needs.
No checked exceptions
Here's the big one for Java developers: Kotlin has no checked exceptions. Every exception is unchecked. You are
never forced to declare throws or wrap a call in try/catch:
fun readConfig(path: String): String {
return File(path).readText() // this can throw IOException - but Kotlin doesn't force you to handle it
}In Java, calling something that throws a checked IOException forces a try/catch or a throws clause. Kotlin
removes that compulsion entirely - you may catch exceptions (with the same try/catch, which is also an
expression), but the compiler never requires it.
This is a deliberate design decision based on years of Java experience: checked exceptions, in practice, often led
to swallowed exceptions (empty catch blocks to shut the compiler up), noisy throws signatures that
propagate up call stacks, and awkwardness with lambdas and functional code (a checked exception in a Stream
lambda is famously painful). Kotlin's designers judged the costs to outweigh the benefits. The trade-off: you lose
the compiler's reminder that a call can fail, so you must rely on documentation and discipline to handle errors
where it matters - the responsibility shifts from the compiler to you.
No checked exceptions means no compiler safety net for errors
The flip side of freedom from try/catch boilerplate is that Kotlin won't remind you a call can fail. A Java method's throws IOException is invisible pressure to handle it; in Kotlin that pressure is gone, so it's easier to forget error handling entirely. Be deliberate: handle exceptions at meaningful boundaries, document what your functions can throw (KDoc's @throws), and don't let 'the compiler isn't making me' become 'nobody handles it.' Many Kotlin codebases also lean on result-returning types (a sealed Result type, or the stdlib's Result) instead of exceptions for expected failures - a pattern the sealed-classes module revisits.
Java's checked exceptions are like a workshop where the law forces a warning sign on every tool that could cut you ('this saw THROWS injuries - acknowledge before use'). Well-intentioned, but the workshop ends up plastered with signs, workers rubber-stamp them without reading (empty catch blocks), and some signs get in the way of actually working (checked exceptions in lambdas). Kotlin's workshop removes the mandatory signs: the saws are exactly as sharp and can still cut you (exceptions still happen), but you're trusted to know that and to put up a warning where it genuinely helps (catch and document where it matters). The upside is a cleaner, less cluttered shop; the responsibility is that safety now depends on your judgment and documentation, not a legal sign on every tool.
You're porting a Java method public String fetch(String url) throws IOException that callers currently must
wrap in try/catch. In Kotlin: (1) does the signature still need to declare the exception, and must callers catch
it? (2) You have a class PaymentProcessor you want visible to your whole billing module but NOT to other
modules or the public API - which visibility modifier? (3) What discipline replaces the compiler's lost 'this can
throw' reminder?
What is a key difference in Kotlin's exception handling compared to Java?
Key takeaways
- Kotlin packages work like Java's, but a file's package need not mirror its directory, and a file can hold many top-level declarations; you can import functions/properties and use import aliases (as).
- Visibility modifiers: public (the default, unlike Java's package-private), private (file or class scope), protected (class + subclasses), and internal (visible across the whole module).
- internal fills a real gap - expose something module-wide while keeping it out of the public API other modules consume; there is no package-private in Kotlin.
- Kotlin has no checked exceptions: every exception is unchecked, so you're never forced to declare throws or wrap calls in try/catch.
- That trades away boilerplate (and Java's swallowed-exception/lambda problems) for the compiler's lost 'this can throw' reminder - compensate with docs, boundary handling, and result types.