{"id":34112,"date":"2022-12-11T17:34:33","date_gmt":"2022-12-11T16:34:33","guid":{"rendered":"https:\/\/www.happycoders.eu\/?p=34112"},"modified":"2025-06-12T08:28:05","modified_gmt":"2025-06-12T06:28:05","slug":"scoped-values","status":"publish","type":"post","link":"https:\/\/www.happycoders.eu\/de\/java\/scoped-values\/","title":{"rendered":"Scoped Values in Java"},"content":{"rendered":"\n<p><em>Scoped Values<\/em> wurden \u2013 zusammen mit <a href=\"\/de\/java\/virtual-threads\/\">virtuellen Threads<\/a> und <a href=\"\/de\/java\/structured-concurrency-structuredtaskscope\/\">Structured Concurrency<\/a> \u2013 in <a href=\"https:\/\/openjdk.org\/projects\/loom\/\" target=\"_blank\" rel=\"noopener\">Project Loom<\/a> entwickelt. Sie sind seit <a href=\"\/de\/java\/java-20-features\/\">Java 20<\/a> als Incubator-Feature und seit <a href=\"\/de\/java\/java-21-features\/\">Java 21<\/a> als Preview Feature im JDK enthalten. In <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-25-features\/\">Java 25<\/a> wurde sie ohne \u00c4nderungen finalisiert.<\/p>\n\n\n\n<p>In diesem Artikel erf\u00e4hrst du:<\/p>\n\n\n\n<ul class=\"wp-block-list hc-checked-list\">\n<li>Was ist ein <em>Scoped Value<\/em>?<\/li>\n\n\n\n<li>Wie verwendet man die <code>ScopedValue<\/code>-Klasse?<\/li>\n\n\n\n<li>Wie werden <em>Scoped Values<\/em> vererbt?<\/li>\n\n\n\n<li>Was ist der Unterschied zwischen <code>ScopedValue<\/code> und <code>ThreadLocal<\/code>?<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"was-ist-ein-scoped-value\">Was ist ein Scoped Value?<\/h2>\n\n\n\n<p><em>Scoped Values<\/em> sind eine Form von impliziten Methodenparametern, die es erm\u00f6glichen, einen oder mehrere Werte (d. h. beliebige Objekte) an eine oder mehrere weit entfernte Methoden zu \u00fcbergeben, ohne sie als explizite Parameter zu jeder Methode in der Aufrufkette hinzuf\u00fcgen zu m\u00fcssen.<\/p>\n\n\n\n<p><em>Scoped Values<\/em> werden in der Regel als \u00f6ffentliche statische Felder angelegt, so dass sie von beliebigen Methoden aus abrufbar sind.<\/p>\n\n\n\n<p>Verwenden mehrere Threads dasselbe statische <code>ScopedValue<\/code>-Feld, dann kann dieses aus Sicht eines jeden Threads einen anderen Wert enthalten.<\/p>\n\n\n\n<p>Falls du mit <code>ThreadLocal<\/code>-Variablen vertraut bist, kommt dir das sicher bekannt vor. Tats\u00e4chlich stellen <em>Scoped Values<\/em> eine moderne Alternative f\u00fcr Thread Locals dar.<\/p>\n\n\n\n<p>Am besten lassen sich <em>Scoped Values<\/em> an einem Beispiel erkl\u00e4ren.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"scopedvalue-beispiel\">ScopedValue Beispiel<\/h2>\n\n\n\n<p>Ein klassisches Einsatzszenario ist ein Web-Framework, das bei einem eingehenden Request den User authentifiziert und die Daten des eingeloggten Users demjenigen Code, der den Request abarbeitet, zur Verf\u00fcgung stellt.<\/p>\n\n\n\n<p>Das kann z. B. ganz klassisch \u00fcber ein Methodenargument funktionieren.<\/p>\n\n\n\n<p>Nun kann sich in komplexen Anwendungen die Abarbeitung eines Requests \u00fcber Hunderte von Methoden erstrecken \u2013 doch die Information \u00fcber den eingeloggten User wird evtl. nur in wenigen Methoden ben\u00f6tigt. Trotzdem m\u00fcssten wir den User durch alle Methoden durchschleifen, die irgendwann zum Aufruf einer Methode f\u00fchren, f\u00fcr die der eingeloggte User relevant ist. <\/p>\n\n\n\n<p>Im folgenden Beispiel wird der eingeloggte User vom <code>Server<\/code> \u00fcber den <code>RestAdapter<\/code> und den <code>UseCase<\/code> bis hin zum <code>Repository<\/code> durchgereicht, wo er schlie\u00dflich ausgewertet wird:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Server<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">serve<\/span><span class=\"hljs-params\">(Request request)<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    User user = authenticateUser(request);\n    restAdapter.processRequest(request, user);\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">RestAdapter<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">processRequest<\/span><span class=\"hljs-params\">(Request request, User loggedInUser)<\/span> <\/span>{ \n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    UUID id = extractId(request);\n    useCase.invoke(id, loggedInUser);\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UseCase<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">invoke<\/span><span class=\"hljs-params\">(UUID id, User loggedInUser)<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    Data data = repository.getData(id, loggedInUser);\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Repository<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Data <span class=\"hljs-title\">getData<\/span><span class=\"hljs-params\">(UUID id, User loggedInUser)<\/span> <\/span>{\n    Data data = findById(id);\n    <span class=\"hljs-keyword\">if<\/span> (loggedInUser.isAdmin()) {\n      enrichDataWithAdminInfos(data);\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Der zus\u00e4tzliche Parameter <code>loggedInUser<\/code> macht unseren Code sehr schnell un\u00fcbersichtlich. Die meisten der Methoden ben\u00f6tigen den User gar nicht \u2013 und es k\u00f6nnte sogar Methoden geben, denen wir aus Sicherheitsgr\u00fcnden den Zugriff auf den User gar nicht erst erlauben wollen.<\/p>\n\n\n\n<p>Und was w\u00e4re, wenn wir an einer Stelle tief im Aufruf-Stack auch noch die IP-Adresse des Users ben\u00f6tigten? Dann m\u00fcssten wir ein weiteres Argument durch zahllose Methoden durchschleifen.<\/p>\n\n\n\n<p>Die Alternative ist es, den User in einem <em>Scoped Value<\/em> zu speichern.<\/p>\n\n\n\n<p>Das funktioniert wie folgt:<\/p>\n\n\n\n<p>Wir legen an \u00f6ffentlich zug\u00e4nglicher Stelle ein statisches Feld vom Typ <code>ScopedValue<\/code> an. Mit <code>ScopedValue.where(...)<\/code> binden wir den <em>Scoped Value<\/em> an den konkreten User, und der <code>ScopedValue.run()<\/code>-Methode \u00fcbergeben wir \u2013 in Form eines&nbsp;<code>Runnable<\/code>&nbsp;\u2013 den Code, f\u00fcr dessen Aufrufdauer der <em>Scoped Value<\/em> g\u00fcltig sein soll:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Server<\/span> <\/span>{\n  <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-keyword\">static<\/span> ScopedValue&lt;User&gt; LOGGED_IN_USER = ScopedValue.newInstance();\n \n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">serve<\/span><span class=\"hljs-params\">(Request request)<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    User loggedInUser = authenticateUser(request);\n    ScopedValue.where(LOGGED_IN_USER, loggedInUser)\n               .run(() -&gt; restAdapter.processRequest(request));\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-uagb-info-box uagb-block-76088612 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top hc-infobox\"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\">Bis einschlie\u00dflich <a href=\"https:\/\/www.happycoders.eu\/java\/java-23-features\/#Scoped_Values_Third_Preview_JEP_481\">Java 23<\/a> k\u00f6nnen wir alternativ die Convenience-Methode <code>runWhere()<\/code> verwenden und das <code>Runnable<\/code> dieser Methode als dritten Parameter \u00fcbergeben:<br><br><code>ScopedValue.runWhere(<br>&nbsp; LOGGED_IN_USER,<br>&nbsp; loggedInUser,<br>&nbsp; () -&gt; restAdapter.processRequest(request) \/\/ \u27f5 the Runnable as 3rd parameter<br>);<\/code><br><br>Diese Variante wurde in <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-24-features\/#Scoped_Values_Fourth_Preview_JEP_487\">Java 24<\/a> entfernt, um die <code>ScopedValue<\/code>-Schnittstellt komplett \u201efluent\u201c zu gestalten.<\/p><\/div><\/div>\n\n\n\n<p>Den <code>loggedInUser<\/code>-Parameter k\u00f6nnen wir dann aus allen Methodensignaturen entfernen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">RestAdapter<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">processRequest<\/span><span class=\"hljs-params\">(Request request)<\/span> <\/span>{ \n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    UUID id = extractId(request);\n    useCase.invoke(id);\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UseCase<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">invoke<\/span><span class=\"hljs-params\">(UUID id)<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    Data data = repository.getData(id);\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Und dort, wo wir den eingeloggten User ben\u00f6tigen, k\u00f6nnen wir ihn mit <code>ScopedValue.get()<\/code> auslesen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Repository<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Data <span class=\"hljs-title\">getData<\/span><span class=\"hljs-params\">(UUID id)<\/span> <\/span>{\n    Data data = findById(id);\n    User loggedInUser = Server.LOGGED_IN_USER.get();\n    <span class=\"hljs-keyword\">if<\/span> (loggedInUser.isAdmin()) {\n      enrichDataWithAdminInfos(data);\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Das macht den Code deutlich les- und wartbarer, da wir den eingeloggten User nicht mehr von einer Methode zur n\u00e4chsten durchreichen m\u00fcssen, sondern genau dort auf ihn zugreifen k\u00f6nnen, wo wir ihn brauchen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"aufruf-einer-methode-mit-rueckgabewert\">Aufruf einer Methode mit R\u00fcckgabewert<\/h3>\n\n\n\n<p>Wenn der aufgerufene Code einen R\u00fcckgabewert hat, kannst du nach <code>ScopedValue.where()<\/code> anstelle von <code>run(Runnable op)<\/code> die Methode <code>call(CallableOp op)<\/code> aufrufen.<\/p>\n\n\n\n<p><code>CallableOp<\/code> ist ein funktionelles, generisches Interface, das wie folgt definiert ist:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@FunctionalInterface<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">CallableOp<\/span>&lt;<span class=\"hljs-title\">T<\/span>, <span class=\"hljs-title\">X<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Throwable<\/span>&gt; <\/span>{\n    <span class=\"hljs-function\">T <span class=\"hljs-title\">call<\/span><span class=\"hljs-params\">()<\/span> <span class=\"hljs-keyword\">throws<\/span> X\n}<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Das Interface enth\u00e4lt neben dem R\u00fcckgabewert auch eine m\u00f6glicherweise geworfene Exception als Typ-Parameter. Somit kann der Compiler erkennen, welche Art von Exception der Aufruf von <code>call(...)<\/code> werfen kann.<\/p>\n\n\n\n<p>Wenn wir also z. B. folgende Methode im Kontext eines <em>Scoped Values<\/em> aufrufen wollen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\">Result <span class=\"hljs-title\">doSomethingSmart<\/span><span class=\"hljs-params\">()<\/span> <span class=\"hljs-keyword\">throws<\/span> SpecificException <\/span>{\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Dann erkennt der Compiler, dass auch <code>call()<\/code> nur eine <code>SpecificException<\/code> werfen kann, und wir k\u00f6nnen sie wie folgt abfangen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">try<\/span> {\n  Result result = ScopedValue.where(USER, loggedInUser).call(() -&gt; doSomethingSmart());\n} <span class=\"hljs-keyword\">catch<\/span> (SpecificException e) {  <span class=\"hljs-comment\">\/\/ \u27f5 Catching SpecificException<\/span>\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Und wenn die aufgerufene keine Exception wirft, brauchen wir auch keine abzufangen. <\/p>\n\n\n\n<div class=\"wp-block-uagb-info-box uagb-block-677d1316 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top hc-infobox\"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\">In Java 21 und 22 existierte das Interface <code>CallableOp<\/code> noch nicht. Dort wurde stattdessen ein <code>Callable<\/code> verwendet. Die <code>call()<\/code>-Methode des <code>Callable<\/code>-Interfaces wirft allerdings eine generische Exception:<br><br><code>@FunctionalInterface<br>public interface Callable&lt;V&gt; {<br>&nbsp; V call() throws Exception;<br>}<\/code><br><br>Und somit musste in Java 21 und 22 beim Aufruf von <code>ScopedValue.call()<\/code> auch immer eine <code>Exception<\/code> abgefangen werden \u2013 selbst wenn die aufgerufene Methode nur eine spezifische oder gar keine Exception werfen konnte.<\/p><\/div><\/div>\n\n\n\n<div class=\"wp-block-uagb-info-box uagb-block-c060314b uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top hc-infobox\"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\">Bis Java 23 konnten wir alternativ die Convenience-Methode <code>callWhere()<\/code> verwenden. Diese wurde in Java 24 gemeinsam mit <code>runWhere()<\/code> (s. o.) entfernt.<\/p><\/div><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"preview-features-vor-java-25-aktivieren\">Preview-Features vor Java 25 aktivieren<\/h3>\n\n\n\n<p>Falls du <em>Scoped Values<\/em> vor <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-25-features\/\">Java 25<\/a> einsetzen m\u00f6chtest, musst du Preview-Features explizit freischalten. Dazu musst du die <code>java<\/code>- und <code>javac<\/code>-Kommandos mit folgenden VM-Optionen aufrufen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">$ javac --enable-preview --source &lt;Java version&gt; &lt;.java file to compile&gt;\n$ java --enable-preview &lt;.java file or compiled class to execute&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div class=\"convertkit-form wp-block-convertkit-form\" style=\"\"><script async data-uid=\"1427197203\" src=\"https:\/\/happycoders.kit.com\/1427197203\/index.js\" data-jetpack-boost=\"ignore\" data-no-defer=\"1\" data-no-optimize=\"1\" nowprocket><\/script><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"rebinding-von-scoped-values\">Rebinding von Scoped Values<\/h2>\n\n\n\n<p><code>ScopedValue<\/code> hat keine <code>set(...)<\/code>-Methode, um den gespeicherten Wert zu \u00e4ndern. Dies ist beabsichtigt, da die Unver\u00e4nderlichkeit eines Wertes komplexen Code deutlich les- und wartbarer macht.<\/p>\n\n\n\n<p>Stattdessen kannst du den Wert f\u00fcr den Aufruf eines begrenzten Code-Abschnitts (z. B. f\u00fcr den Aufruf einer Untermethode) neu binden (\u201eRebinding\u201c auf englisch). D. h. dass f\u00fcr diesen begrenzten Code-Abschnitt ein anderer Wert sichtbar ist ... und sobald dieser Abschnitt beendet wird, wieder der urspr\u00fcngliche.<\/p>\n\n\n\n<p>So k\u00f6nnte unsere <code>RestAdapter<\/code>-Methode z. B. die Informationen \u00fcber den eingeloggten User vor der <code>extractId(<code>...<\/code>)<\/code>-Methode verbergen wollen. Dazu k\u00f6nnen wir erneut <code>ScopedValue.where(...)<\/code> aufrufen und w\u00e4hrend des Aufrufs der Untermethode den eingeloggten User auf <code>null<\/code> setzen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">RestAdapter<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">processRequest<\/span><span class=\"hljs-params\">(Request request)<\/span> <\/span>{ \n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    UUID id = ScopedValue.where(LOGGED_IN_USER, <span class=\"hljs-keyword\">null<\/span>)\n                         .call(() -&gt; extractId(request));\n    useCase.invoke(id);\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Hier siehst du auch, wie wir anstelle von <code>run(...)<\/code> die <code>call(...)<\/code>-Methode verwenden und ein <code>Callable<\/code> (also eine Methode mit R\u00fcckgabewert) anstelle eines <code>Runnable<\/code>s \u00fcbergeben.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"vererbung-von-scoped-values\">Vererbung von Scoped Values<\/h2>\n\n\n\n<p><em>Scoped Values<\/em> werden automatisch an alle Kind-Threads vererbt, die \u00fcber einen <a href=\"\/de\/java\/structured-concurrency-structuredtaskscope\/\">Structured Task Scope<\/a> erzeugt werden.<\/p>\n\n\n\n<p>Mittels <code>StructuredTaskScope<\/code> k\u00f6nnte unser Use Case z. B. parallel zur Repository-Methode noch einen externen Service aufrufen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UseCase<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">invoke<\/span><span class=\"hljs-params\">(UUID id)<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = <span class=\"hljs-keyword\">new<\/span> StructuredTaskScope.ShutdownOnFailure()) {\n      Future&lt;Data&gt;    dataFuture    = scope.fork(() -&gt; repository.getData(id));\n      Future&lt;ExtData&gt; extDataFuture = scope.fork(() -&gt; remoteService.getExtData(id));\n \n      scope.join();\n      scope.throwIfFailed();\n\n      Data    data    = dataFuture.resultNow();\n      ExtData extData = extDataFuture.resultNow();\n      <span class=\"hljs-comment\">\/\/ ...<\/span>\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>So kann aus den Kind-Threads, die per <code>fork(...)<\/code> erstellt werden, ebenfalls per <code>LOGGED_IN_USER.get()<\/code> auf den eingeloggten User zugegriffen werden.<\/p>\n\n\n\n<p>Da der <code>StructuredTaskScope<\/code> erst abgeschlossen ist, wenn alle Kind-Threads beendet sind, passt dieser sehr gut in das Konzept der <em>Scoped Values<\/em>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"was-ist-der-unterschied-zwischen-scopedvalue-und-threadlocal\">Was ist der Unterschied zwischen ScopedValue und ThreadLocal?<\/h2>\n\n\n\n<p>Wer die Anforderungen dieser Beispiele bisher mit Thread-Local-Variablen gel\u00f6st hat, wird sich nun vielleicht fragen: Warum brauchen wir <em>Scoped Values<\/em>? Was k\u00f6nnen sie, was Thread Locals nicht k\u00f6nnen?<\/p>\n\n\n\n<p><em>Scoped Values<\/em> haben folgende Vorteile:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sie sind nur w\u00e4hrend der Laufzeit des an die <code>run(...)<\/code>-Methode \u00fcbergebenen <code>Runnable<\/code>s g\u00fcltig und werden danach (sofern keine weiteren Referenzen auf sie existieren) zur Garbage Collection freigegeben. Ein Thread-Local-Wert hingegen bleibt solange im Speicher, bis entweder der Thread beendet wird (was bei der Verwendung eines Thread-Pools u. U. niemals der Fall ist) oder er explizit mit <code>ThreadLocal.remove()<\/code> gel\u00f6scht wird. Da viele Entwickler vergessen, das zu tun (oder es nicht tun, weil das Programm so komplex ist, dass nicht erkennbar ist, wann ein Thread-Local-Wert nicht mehr ben\u00f6tigt wird), sind Memory Leaks oft die Folge.<\/li>\n\n\n\n<li>Ein <em>Scoped Value<\/em> ist unver\u00e4nderlich \u2013 er kann nur durch das oben erw\u00e4hnte Rebinding f\u00fcr einen neuen Scope neu gesetzt werden. Das verbessert die Verst\u00e4ndlichkeit und Wartbarkeit des Codes erheblich gegen\u00fcber Thread Locals, dir jederzeit durch <code>set()<\/code> ver\u00e4ndert werden k\u00f6nnen.<\/li>\n\n\n\n<li>Die durch <code>StructuredTaskScope<\/code> erzeugten Kind-Threads haben Zugriff auf den <em>Scoped Value<\/em> des Eltern-Threads. Wenn wir hingegen <code>InheritableThreadLocal<\/code> verwenden, wird dessen Wert in jeden Kind-Thread <em>kopiert<\/em>, damit ein Kind-Thread nicht den Thread-Local-Wert des Eltern-Threads ver\u00e4ndern kann. Dies kann den Speicherbedarf erheblich erh\u00f6hen.<\/li>\n<\/ul>\n\n\n\n<p>Genau wie Thread Locals stehen auch <em>Scoped Values<\/em> sowohl f\u00fcr Plattform-Threads als auch f\u00fcr <a href=\"https:\/\/www.happycoders.eu\/de\/java\/virtual-threads\/\">virtuelle Threads<\/a> zur Verf\u00fcgung. Insbesondere bei Tausenden bis Millionen von virtuellen Kind-Threads kann die Speicherersparnis durch den Zugriff auf den <em>Scoped Value<\/em> des Eltern-Threads (anstelle der Erstellung einer Kopie) erheblich sein.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"fazit\">Fazit<\/h2>\n\n\n\n<p>Mit <em>Scoped Values<\/em> erhalten wir ein sehr n\u00fctzliches Konstrukt, um einem Thread und ggf. einer Gruppe von Kind-Threads f\u00fcr deren Lebensdauer einen nur lesbaren, Thread-spezifischen Wert zur Verf\u00fcgung zu stellen.<\/p>\n\n\n\n<p>Bitte beachte, dass sich <em>Scoped Values<\/em> bis Java 24 noch im Preview-Stadium befinden und explizit \u00fcber die VM-Option <code>-enable-preview --source &lt;Java-Version&gt;<\/code> aktiviert werden m\u00fcssen.<\/p>\n<aside><p>Wenn dir der Artikel weitergeholfen hat, w\u00fcrde ich mich sehr \u00fcber eine positive Bewertung auf meinem <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">ProvenExpert-Profil<\/a> freuen. Dein Feedback hilft mir, meine Inhalte weiter zu verbessern und motiviert mich, neue informative Artikel zu schreiben.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">Bewertung abgeben<\/a><\/p>\r\n                        <p>Du m\u00f6chtest \u00fcber alle neue Java-Features auf dem Laufenden sein? Dann <a href=\"#\" data-formkit-toggle=\"d8ee997126\">klicke hier<\/a>, um dich f\u00fcr den HappyCoders-Newsletter anzumelden.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"#\" data-formkit-toggle=\"d8ee997126\">Newsletter-Anmeldung<\/a><\/p><\/aside>","protected":false},"excerpt":{"rendered":"<p>Was ist ein Scoped Value? Wie setzt man ScopedValue ein? Wie werden ScopedValues vererbt? Was ist der Unterschied zwischen ScopedValue und ThreadLocal?<\/p>\n","protected":false},"author":1,"featured_media":34183,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_titles_title":"","_seopress_titles_desc":"Was ist ein Scoped Value? Wie setzt man ScopedValue ein? Was ist der Unterschied zwischen ScopedValue und ThreadLocal?","_seopress_robots_index":"","_seopress_robots_follow":"","_seopress_robots_imageindex":"","_seopress_robots_snippet":"","_seopress_robots_primary_cat":"","_seopress_robots_breadcrumbs":"","_seopress_robots_freeze_modified_date":"","_seopress_robots_custom_modified_date":"","_seopress_robots_canonical":"","_seopress_social_fb_title":"","_seopress_social_fb_desc":"","_seopress_social_fb_img":"","_seopress_social_fb_img_attachment_id":0,"_seopress_social_fb_img_width":0,"_seopress_social_fb_img_height":0,"_seopress_social_twitter_title":"","_seopress_social_twitter_desc":"","_seopress_social_twitter_img":"","_seopress_social_twitter_img_attachment_id":0,"_seopress_social_twitter_img_width":0,"_seopress_social_twitter_img_height":0,"_seopress_redirections_value":"","_seopress_redirections_enabled":"","_seopress_redirections_enabled_regex":"","_seopress_redirections_logged_status":"","_seopress_redirections_param":"","_seopress_redirections_type":0,"_seopress_analysis_target_kw":"ScopedValue,scoped values","_seopress_news_disabled":"","_seopress_video_disabled":"","_seopress_video":[],"_seopress_pro_schemas_manual":[],"_seopress_pro_rich_snippets_disable_all":"","_seopress_pro_rich_snippets_disable":[],"_seopress_pro_schemas":[],"_uag_custom_page_level_css":"","_wp_convertkit_post_meta":{"form":"-1","landing_page":"","tag":"0","restrict_content":"0"},"_metis_text_type":"standard","_metis_text_length":11966,"_post_count":0,"footnotes":""},"categories":[64],"tags":[220],"class_list":["post-34112","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","tag-java-nebenlaeufigkeit"],"uagb_featured_image_src":{"full":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java.jpg",1770,986,false],"thumbnail":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java.jpg",150,84,false],"medium":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java.jpg",300,167,false],"medium_large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java.jpg",768,428,false],"large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java.jpg",1024,570,false],"feature_thumb_224":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-224x125.jpg",224,125,true],"feature_thumb_336":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-336x187.jpg",336,187,true],"feature_thumb_504":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-504x281.jpg",504,281,true],"feature_thumb_672":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-672x374.jpg",672,374,true],"half_400":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-400x223.jpg",400,223,true],"half_600":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-600x334.jpg",600,334,true],"full_800":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-800x446.jpg",800,446,true],"full_944":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-944x526.jpg",944,526,true],"full_1200":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-1200x668.jpg",1200,668,true],"wide_1180":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-1180x490.jpg",1180,490,true],"wide_1770":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java-1770x735.jpg",1770,735,true],"1536x1536":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java.jpg",1536,856,false],"2048x2048":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/scoped-values-java.jpg",1770,986,false]},"uagb_author_info":{"display_name":"Sven Woltmann","author_link":"https:\/\/www.happycoders.eu\/de\/author\/sven\/"},"uagb_comment_info":0,"uagb_excerpt":"Was ist ein Scoped Value? Wie setzt man ScopedValue ein? Wie werden ScopedValues vererbt? Was ist der Unterschied zwischen ScopedValue und ThreadLocal?","public_identification_id":"228930d0a46b497e9f03c726c12ff175","private_identification_id":"3b546d2f025c45c3863bbd3c2d082b8a","_links":{"self":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/34112","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/comments?post=34112"}],"version-history":[{"count":10,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/34112\/revisions"}],"predecessor-version":[{"id":52405,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/34112\/revisions\/52405"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media\/34183"}],"wp:attachment":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media?parent=34112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/categories?post=34112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/tags?post=34112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}