[{"data":1,"prerenderedAt":1892},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-lambdas-functional-interfaces":3},{"id":4,"title":5,"body":6,"description":1878,"difficulty":1879,"extension":1880,"framework":1881,"frameworkSlug":48,"meta":1882,"navigation":181,"order":57,"path":1883,"qaPath":1884,"seo":1885,"stem":1886,"subtopic":1887,"topic":1888,"topicSlug":1889,"updated":1890,"__hash__":1891},"blog\u002Fblog\u002Fjava-lambdas-functional-interfaces.md","Java Lambdas & Functional Interfaces — A Practical Guide",{"type":7,"value":8,"toc":1865},"minimark",[9,14,27,31,43,221,238,242,272,389,402,406,415,581,698,723,727,776,959,972,976,989,1066,1133,1151,1155,1174,1298,1316,1320,1340,1419,1489,1509,1513,1528,1616,1630,1634,1660,1727,1771,1790,1794,1861],[10,11,13],"h2",{"id":12},"functional-programming-the-java-way","Functional programming, the Java way",[15,16,17,18,22,23,26],"p",{},"Before ",[19,20,21],"strong",{},"Java 8",", passing behavior around meant writing a verbose anonymous class for every\ncallback, comparator, or event handler. Lambdas changed that: behavior became a value you\ncould store in a variable, pass as an argument, and return from a method. But a lambda is\nnot a free-floating closure like in other languages — it is always tied to a ",[19,24,25],{},"functional\ninterface",", and understanding that link explains almost every rule that follows. This\nguide walks through the syntax, the standard interface library, composition, method\nreferences, and the subtle differences from the anonymous classes lambdas replaced.",[10,28,30],{"id":29},"what-a-lambda-is","What a lambda is",[15,32,33,34,37,38,42],{},"A ",[19,35,36],{},"lambda expression"," is an anonymous function: a parameter list, an arrow, and a body.\nIt is compact syntax for implementing the single abstract method of a functional interface.\nThe compiler matches the lambda's shape to that method, so the body ",[39,40,41],"em",{},"becomes"," the method\nimplementation — no class name, no boilerplate.",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-java shiki shiki-themes github-light github-dark","\u002F\u002F parameters -> body\nRunnable r = () -> System.out.println(\"hi\");   \u002F\u002F no params, expression body\nFunction\u003CInteger,Integer> sq = x -> x * x;     \u002F\u002F one param, parens optional\nComparator\u003CString> c = (a, b) -> a.length() - b.length(); \u002F\u002F multi-param needs parens\n\n\u002F\u002F block body needs braces AND an explicit return\nBinaryOperator\u003CInteger> add = (a, b) -> { return a + b; };\n","java","",[51,52,53,62,99,134,176,183,189],"code",{"__ignoreMap":49},[54,55,58],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"sJ8bj","\u002F\u002F parameters -> body\n",[54,63,65,69,73,76,79,82,86,89,93,96],{"class":56,"line":64},2,[54,66,68],{"class":67},"sVt8B","Runnable r ",[54,70,72],{"class":71},"szBVR","=",[54,74,75],{"class":67}," () ",[54,77,78],{"class":71},"->",[54,80,81],{"class":67}," System.out.",[54,83,85],{"class":84},"sScJk","println",[54,87,88],{"class":67},"(",[54,90,92],{"class":91},"sZZnC","\"hi\"",[54,94,95],{"class":67},");   ",[54,97,98],{"class":60},"\u002F\u002F no params, expression body\n",[54,100,102,105,108,111,113,116,118,121,123,125,128,131],{"class":56,"line":101},3,[54,103,104],{"class":67},"Function\u003C",[54,106,107],{"class":71},"Integer",[54,109,110],{"class":67},",",[54,112,107],{"class":71},[54,114,115],{"class":67},"> sq ",[54,117,72],{"class":71},[54,119,120],{"class":67}," x ",[54,122,78],{"class":71},[54,124,120],{"class":67},[54,126,127],{"class":71},"*",[54,129,130],{"class":67}," x;     ",[54,132,133],{"class":60},"\u002F\u002F one param, parens optional\n",[54,135,137,140,143,146,148,151,153,156,159,162,165,168,170,173],{"class":56,"line":136},4,[54,138,139],{"class":67},"Comparator\u003C",[54,141,142],{"class":71},"String",[54,144,145],{"class":67},"> c ",[54,147,72],{"class":71},[54,149,150],{"class":67}," (a, b) ",[54,152,78],{"class":71},[54,154,155],{"class":67}," a.",[54,157,158],{"class":84},"length",[54,160,161],{"class":67},"() ",[54,163,164],{"class":71},"-",[54,166,167],{"class":67}," b.",[54,169,158],{"class":84},[54,171,172],{"class":67},"(); ",[54,174,175],{"class":60},"\u002F\u002F multi-param needs parens\n",[54,177,179],{"class":56,"line":178},5,[54,180,182],{"emptyLinePlaceholder":181},true,"\n",[54,184,186],{"class":56,"line":185},6,[54,187,188],{"class":60},"\u002F\u002F block body needs braces AND an explicit return\n",[54,190,192,195,197,200,202,204,206,209,212,215,218],{"class":56,"line":191},7,[54,193,194],{"class":67},"BinaryOperator\u003C",[54,196,107],{"class":71},[54,198,199],{"class":67},"> add ",[54,201,72],{"class":71},[54,203,150],{"class":67},[54,205,78],{"class":71},[54,207,208],{"class":67}," { ",[54,210,211],{"class":71},"return",[54,213,214],{"class":67}," a ",[54,216,217],{"class":71},"+",[54,219,220],{"class":67}," b; };\n",[15,222,223,224,227,228,230,231,234,235,237],{},"The two rules worth memorising: a ",[19,225,226],{},"single-expression body"," has no braces and no ",[51,229,211],{},"\n(the value is implicit), while a ",[19,232,233],{},"block body"," needs both braces and an explicit ",[51,236,211],{},"\nif it produces a value. Parameter types are almost always inferred from the target, so you\nomit them.",[10,239,241],{"id":240},"functional-interfaces-sam-and-functionalinterface","Functional interfaces, SAM, and @FunctionalInterface",[15,243,33,244,247,248,251,252,255,256,259,260,263,264,267,268,271],{},[19,245,246],{},"functional interface"," is an interface with exactly ",[19,249,250],{},"one abstract method"," — a ",[19,253,254],{},"SAM","\n(Single Abstract Method). That method is the target a lambda implements. The interface can\nstill declare any number of ",[51,257,258],{},"default"," and ",[51,261,262],{},"static"," methods, and methods inherited from\n",[51,265,266],{},"Object"," (like ",[51,269,270],{},"equals",") don't count toward the limit.",[44,273,275],{"className":46,"code":274,"language":48,"meta":49,"style":49},"@FunctionalInterface\ninterface Transformer {\n  String apply(String s);                                  \u002F\u002F the one abstract method\n  default String twice(String s) { return apply(apply(s)); } \u002F\u002F doesn't break SAM\n  \u002F\u002F String other(String s);   \u002F\u002F adding this would FAIL the build\n}\n\nTransformer upper = s -> s.toUpperCase();\n",[51,276,277,285,296,317,350,358,363,367],{"__ignoreMap":49},[54,278,279,282],{"class":56,"line":57},[54,280,281],{"class":67},"@",[54,283,284],{"class":71},"FunctionalInterface\n",[54,286,287,290,293],{"class":56,"line":64},[54,288,289],{"class":71},"interface",[54,291,292],{"class":84}," Transformer",[54,294,295],{"class":67}," {\n",[54,297,298,301,304,307,311,314],{"class":56,"line":101},[54,299,300],{"class":67},"  String ",[54,302,303],{"class":84},"apply",[54,305,306],{"class":67},"(String ",[54,308,310],{"class":309},"s4XuR","s",[54,312,313],{"class":67},");                                  ",[54,315,316],{"class":60},"\u002F\u002F the one abstract method\n",[54,318,319,322,325,328,330,332,335,337,340,342,344,347],{"class":56,"line":136},[54,320,321],{"class":71},"  default",[54,323,324],{"class":67}," String ",[54,326,327],{"class":84},"twice",[54,329,306],{"class":67},[54,331,310],{"class":309},[54,333,334],{"class":67},") { ",[54,336,211],{"class":71},[54,338,339],{"class":84}," apply",[54,341,88],{"class":67},[54,343,303],{"class":84},[54,345,346],{"class":67},"(s)); } ",[54,348,349],{"class":60},"\u002F\u002F doesn't break SAM\n",[54,351,352,355],{"class":56,"line":178},[54,353,354],{"class":60},"  \u002F\u002F String other(String s);",[54,356,357],{"class":60},"   \u002F\u002F adding this would FAIL the build\n",[54,359,360],{"class":56,"line":185},[54,361,362],{"class":67},"}\n",[54,364,365],{"class":56,"line":191},[54,366,182],{"emptyLinePlaceholder":181},[54,368,370,373,375,378,380,383,386],{"class":56,"line":369},8,[54,371,372],{"class":67},"Transformer upper ",[54,374,72],{"class":71},[54,376,377],{"class":67}," s ",[54,379,78],{"class":71},[54,381,382],{"class":67}," s.",[54,384,385],{"class":84},"toUpperCase",[54,387,388],{"class":67},"();\n",[15,390,391,392,397,398,401],{},"The ",[19,393,394],{},[51,395,396],{},"@FunctionalInterface"," annotation is optional and has no runtime effect — a lambda\nworks on any SAM interface whether or not it's annotated. What it buys you is a ",[19,399,400],{},"compile\nerror"," if someone later adds a second abstract method, protecting every caller who passed\na lambda. Treat it as documentation that the compiler enforces.",[10,403,405],{"id":404},"the-javautilfunction-family","The java.util.function family",[15,407,408,409,414],{},"You rarely need to write your own functional interface, because ",[19,410,411],{},[51,412,413],{},"java.util.function","\nships the general-purpose ones. They cluster into four families — transform, produce,\nconsume, test — plus operator and two-argument variants.",[416,417,418,437],"table",{},[419,420,421],"thead",{},[422,423,424,428,431,434],"tr",{},[425,426,427],"th",{},"Interface",[425,429,430],{},"Abstract method",[425,432,433],{},"Shape",[425,435,436],{},"Used by",[438,439,440,461,481,501,521,541,561],"tbody",{},[422,441,442,448,453,456],{},[443,444,445],"td",{},[51,446,447],{},"Function\u003CT,R>",[443,449,450],{},[51,451,452],{},"R apply(T)",[443,454,455],{},"T → R",[443,457,458],{},[51,459,460],{},"Stream.map",[422,462,463,468,473,476],{},[443,464,465],{},[51,466,467],{},"BiFunction\u003CT,U,R>",[443,469,470],{},[51,471,472],{},"R apply(T,U)",[443,474,475],{},"(T,U) → R",[443,477,478],{},[51,479,480],{},"Map.merge",[422,482,483,488,493,496],{},[443,484,485],{},[51,486,487],{},"Supplier\u003CT>",[443,489,490],{},[51,491,492],{},"T get()",[443,494,495],{},"() → T",[443,497,498],{},[51,499,500],{},"Optional.orElseGet",[422,502,503,508,513,516],{},[443,504,505],{},[51,506,507],{},"Consumer\u003CT>",[443,509,510],{},[51,511,512],{},"void accept(T)",[443,514,515],{},"T → void",[443,517,518],{},[51,519,520],{},"forEach",[422,522,523,528,533,536],{},[443,524,525],{},[51,526,527],{},"Predicate\u003CT>",[443,529,530],{},[51,531,532],{},"boolean test(T)",[443,534,535],{},"T → boolean",[443,537,538],{},[51,539,540],{},"Stream.filter",[422,542,543,548,553,556],{},[443,544,545],{},[51,546,547],{},"UnaryOperator\u003CT>",[443,549,550],{},[51,551,552],{},"T apply(T)",[443,554,555],{},"T → T",[443,557,558],{},[51,559,560],{},"List.replaceAll",[422,562,563,568,573,576],{},[443,564,565],{},[51,566,567],{},"BinaryOperator\u003CT>",[443,569,570],{},[51,571,572],{},"T apply(T,T)",[443,574,575],{},"(T,T) → T",[443,577,578],{},[51,579,580],{},"Stream.reduce",[44,582,584],{"className":46,"code":583,"language":48,"meta":49,"style":49},"Function\u003CString,Integer> length = String::length;   \u002F\u002F transform\nSupplier\u003CDouble>         rng    = Math::random;      \u002F\u002F produce\nConsumer\u003CString>         print  = System.out::println; \u002F\u002F consume\nPredicate\u003CInteger>       isEven = n -> n % 2 == 0;   \u002F\u002F test\n",[51,585,586,613,637,660],{"__ignoreMap":49},[54,587,588,590,592,594,596,599,601,604,607,610],{"class":56,"line":57},[54,589,104],{"class":67},[54,591,142],{"class":71},[54,593,110],{"class":67},[54,595,107],{"class":71},[54,597,598],{"class":67},"> length ",[54,600,72],{"class":71},[54,602,603],{"class":67}," String",[54,605,606],{"class":71},"::",[54,608,609],{"class":67},"length;   ",[54,611,612],{"class":60},"\u002F\u002F transform\n",[54,614,615,618,621,624,626,629,631,634],{"class":56,"line":64},[54,616,617],{"class":67},"Supplier\u003C",[54,619,620],{"class":71},"Double",[54,622,623],{"class":67},">         rng    ",[54,625,72],{"class":71},[54,627,628],{"class":67}," Math",[54,630,606],{"class":71},[54,632,633],{"class":67},"random;      ",[54,635,636],{"class":60},"\u002F\u002F produce\n",[54,638,639,642,644,647,649,652,654,657],{"class":56,"line":101},[54,640,641],{"class":67},"Consumer\u003C",[54,643,142],{"class":71},[54,645,646],{"class":67},">         print  ",[54,648,72],{"class":71},[54,650,651],{"class":67}," System.out",[54,653,606],{"class":71},[54,655,656],{"class":67},"println; ",[54,658,659],{"class":60},"\u002F\u002F consume\n",[54,661,662,665,667,670,672,675,677,679,682,686,689,692,695],{"class":56,"line":136},[54,663,664],{"class":67},"Predicate\u003C",[54,666,107],{"class":71},[54,668,669],{"class":67},">       isEven ",[54,671,72],{"class":71},[54,673,674],{"class":67}," n ",[54,676,78],{"class":71},[54,678,674],{"class":67},[54,680,681],{"class":71},"%",[54,683,685],{"class":684},"sj4cs"," 2",[54,687,688],{"class":71}," ==",[54,690,691],{"class":684}," 0",[54,693,694],{"class":67},";   ",[54,696,697],{"class":60},"\u002F\u002F test\n",[15,699,700,259,703,706,707,710,711,714,715,718,719,722],{},[51,701,702],{},"UnaryOperator",[51,704,705],{},"BinaryOperator"," are just ",[51,708,709],{},"Function","\u002F",[51,712,713],{},"BiFunction"," specialised so input\nand output share a type — reach for them when that's true (",[51,716,717],{},"String::trim",", ",[51,720,721],{},"Integer::sum",")\nbecause they read more clearly than the general form.",[10,724,726],{"id":725},"composing-functions-and-predicates","Composing functions and predicates",[15,728,729,730,733,734,718,736,741,742,745,746,751,752,755,756,718,759,718,764,769,770,775],{},"Functional interfaces come with ",[19,731,732],{},"default methods"," that combine instances into new ones,\nleaving the originals untouched. For ",[51,735,709],{},[19,737,738],{},[51,739,740],{},"andThen"," runs left-to-right (this,\n",[39,743,744],{},"and then"," that) while ",[19,747,748],{},[51,749,750],{},"compose"," runs inside-out, matching the maths ",[51,753,754],{},"f(g(x))",". For\n",[51,757,758],{},"Predicate",[19,760,761],{},[51,762,763],{},"and",[19,765,766],{},[51,767,768],{},"or",", and ",[19,771,772],{},[51,773,774],{},"negate"," build compound conditions.",[44,777,779],{"className":46,"code":778,"language":48,"meta":49,"style":49},"Function\u003CInteger,Integer> times2 = x -> x * 2;\nFunction\u003CInteger,Integer> plus3  = x -> x + 3;\ntimes2.andThen(plus3).apply(5);   \u002F\u002F (5*2)+3 = 13   — f then g\ntimes2.compose(plus3).apply(5);   \u002F\u002F (5+3)*2 = 16   — g then f\n\nPredicate\u003CString> notBlank = s -> !s.isBlank();\nPredicate\u003CString> shortStr = s -> s.length() \u003C 10;\nPredicate\u003CString> valid = notBlank.and(shortStr); \u002F\u002F && short-circuits\n",[51,780,781,809,837,859,878,882,908,937],{"__ignoreMap":49},[54,782,783,785,787,789,791,794,796,798,800,802,804,806],{"class":56,"line":57},[54,784,104],{"class":67},[54,786,107],{"class":71},[54,788,110],{"class":67},[54,790,107],{"class":71},[54,792,793],{"class":67},"> times2 ",[54,795,72],{"class":71},[54,797,120],{"class":67},[54,799,78],{"class":71},[54,801,120],{"class":67},[54,803,127],{"class":71},[54,805,685],{"class":684},[54,807,808],{"class":67},";\n",[54,810,811,813,815,817,819,822,824,826,828,830,832,835],{"class":56,"line":64},[54,812,104],{"class":67},[54,814,107],{"class":71},[54,816,110],{"class":67},[54,818,107],{"class":71},[54,820,821],{"class":67},"> plus3  ",[54,823,72],{"class":71},[54,825,120],{"class":67},[54,827,78],{"class":71},[54,829,120],{"class":67},[54,831,217],{"class":71},[54,833,834],{"class":684}," 3",[54,836,808],{"class":67},[54,838,839,842,844,847,849,851,854,856],{"class":56,"line":101},[54,840,841],{"class":67},"times2.",[54,843,740],{"class":84},[54,845,846],{"class":67},"(plus3).",[54,848,303],{"class":84},[54,850,88],{"class":67},[54,852,853],{"class":684},"5",[54,855,95],{"class":67},[54,857,858],{"class":60},"\u002F\u002F (5*2)+3 = 13   — f then g\n",[54,860,861,863,865,867,869,871,873,875],{"class":56,"line":136},[54,862,841],{"class":67},[54,864,750],{"class":84},[54,866,846],{"class":67},[54,868,303],{"class":84},[54,870,88],{"class":67},[54,872,853],{"class":684},[54,874,95],{"class":67},[54,876,877],{"class":60},"\u002F\u002F (5+3)*2 = 16   — g then f\n",[54,879,880],{"class":56,"line":178},[54,881,182],{"emptyLinePlaceholder":181},[54,883,884,886,888,891,893,895,897,900,903,906],{"class":56,"line":185},[54,885,664],{"class":67},[54,887,142],{"class":71},[54,889,890],{"class":67},"> notBlank ",[54,892,72],{"class":71},[54,894,377],{"class":67},[54,896,78],{"class":71},[54,898,899],{"class":71}," !",[54,901,902],{"class":67},"s.",[54,904,905],{"class":84},"isBlank",[54,907,388],{"class":67},[54,909,910,912,914,917,919,921,923,925,927,929,932,935],{"class":56,"line":191},[54,911,664],{"class":67},[54,913,142],{"class":71},[54,915,916],{"class":67},"> shortStr ",[54,918,72],{"class":71},[54,920,377],{"class":67},[54,922,78],{"class":71},[54,924,382],{"class":67},[54,926,158],{"class":84},[54,928,161],{"class":67},[54,930,931],{"class":71},"\u003C",[54,933,934],{"class":684}," 10",[54,936,808],{"class":67},[54,938,939,941,943,946,948,951,953,956],{"class":56,"line":369},[54,940,664],{"class":67},[54,942,142],{"class":71},[54,944,945],{"class":67},"> valid ",[54,947,72],{"class":71},[54,949,950],{"class":67}," notBlank.",[54,952,763],{"class":84},[54,954,955],{"class":67},"(shortStr); ",[54,957,958],{"class":60},"\u002F\u002F && short-circuits\n",[15,960,961,962,967,968,971],{},"Composition keeps small, named pieces reusable and lets you assemble complex logic without\nnesting lambdas. Java 11 added a static ",[19,963,964],{},[51,965,966],{},"Predicate.not(...)"," so you can negate a method\nreference directly: ",[51,969,970],{},"filter(Predicate.not(String::isBlank))",".",[10,973,975],{"id":974},"method-references-four-kinds","Method references: four kinds",[15,977,978,979,982,983,985,986,971],{},"When a lambda does nothing but forward its arguments to a single existing method, a\n",[19,980,981],{},"method reference"," (",[51,984,606],{},") says the same thing with less noise. The compiler infers which\nmethod from the target interface's signature. There are exactly ",[19,987,988],{},"four kinds",[416,990,991,1004],{},[419,992,993],{},[422,994,995,998,1001],{},[425,996,997],{},"Kind",[425,999,1000],{},"Syntax",[425,1002,1003],{},"Equivalent lambda",[438,1005,1006,1021,1036,1051],{},[422,1007,1008,1011,1016],{},[443,1009,1010],{},"Static",[443,1012,1013],{},[51,1014,1015],{},"Integer::parseInt",[443,1017,1018],{},[51,1019,1020],{},"s -> Integer.parseInt(s)",[422,1022,1023,1026,1031],{},[443,1024,1025],{},"Bound instance",[443,1027,1028],{},[51,1029,1030],{},"System.out::println",[443,1032,1033],{},[51,1034,1035],{},"s -> System.out.println(s)",[422,1037,1038,1041,1046],{},[443,1039,1040],{},"Unbound instance",[443,1042,1043],{},[51,1044,1045],{},"String::toUpperCase",[443,1047,1048],{},[51,1049,1050],{},"s -> s.toUpperCase()",[422,1052,1053,1056,1061],{},[443,1054,1055],{},"Constructor",[443,1057,1058],{},[51,1059,1060],{},"ArrayList::new",[443,1062,1063],{},[51,1064,1065],{},"() -> new ArrayList\u003C>()",[44,1067,1069],{"className":46,"code":1068,"language":48,"meta":49,"style":49},"String prefix = \"Hello, \";\nFunction\u003CString,String>  greet = prefix::concat;   \u002F\u002F BOUND   — receiver captured now\nFunction\u003CString,Integer> len   = String::length;   \u002F\u002F UNBOUND — receiver is the 1st arg\n",[51,1070,1071,1083,1109],{"__ignoreMap":49},[54,1072,1073,1076,1078,1081],{"class":56,"line":57},[54,1074,1075],{"class":67},"String prefix ",[54,1077,72],{"class":71},[54,1079,1080],{"class":91}," \"Hello, \"",[54,1082,808],{"class":67},[54,1084,1085,1087,1089,1091,1093,1096,1098,1101,1103,1106],{"class":56,"line":64},[54,1086,104],{"class":67},[54,1088,142],{"class":71},[54,1090,110],{"class":67},[54,1092,142],{"class":71},[54,1094,1095],{"class":67},">  greet ",[54,1097,72],{"class":71},[54,1099,1100],{"class":67}," prefix",[54,1102,606],{"class":71},[54,1104,1105],{"class":67},"concat;   ",[54,1107,1108],{"class":60},"\u002F\u002F BOUND   — receiver captured now\n",[54,1110,1111,1113,1115,1117,1119,1122,1124,1126,1128,1130],{"class":56,"line":101},[54,1112,104],{"class":67},[54,1114,142],{"class":71},[54,1116,110],{"class":67},[54,1118,107],{"class":71},[54,1120,1121],{"class":67},"> len   ",[54,1123,72],{"class":71},[54,1125,603],{"class":67},[54,1127,606],{"class":71},[54,1129,609],{"class":67},[54,1131,1132],{"class":60},"\u002F\u002F UNBOUND — receiver is the 1st arg\n",[15,1134,1135,1136,1139,1140,1143,1144,1146,1147,1150],{},"The trap is ",[19,1137,1138],{},"bound vs unbound",". A bound reference puts a ",[39,1141,1142],{},"specific object"," (a value or\nvariable) on the left of ",[51,1145,606],{}," and uses it as the receiver. An unbound reference puts a\n",[39,1148,1149],{},"type name"," on the left, and the lambda's first argument becomes the receiver — so the\ninterface has one extra leading parameter.",[10,1152,1154],{"id":1153},"effectively-final-capture-and-why-it-exists","Effectively-final capture and why it exists",[15,1156,1157,1158,1161,1162,1165,1166,1169,1170,1173],{},"A lambda may ",[19,1159,1160],{},"capture"," local variables from the enclosing method, but only ones that are\n",[19,1163,1164],{},"final or effectively final"," — assigned exactly once and never reassigned, even without\nthe ",[51,1167,1168],{},"final"," keyword. Reassigning a captured local is a compile error. Crucially, this rule\napplies ",[39,1171,1172],{},"only to locals","; instance and static fields are fair game.",[44,1175,1177],{"className":46,"code":1176,"language":48,"meta":49,"style":49},"int factor = 3;                                  \u002F\u002F effectively final\nFunction\u003CInteger,Integer> f = x -> x * factor;   \u002F\u002F OK — captures the value 3\n\nint total = 0;\n\u002F\u002F list.forEach(n -> total += n);                \u002F\u002F COMPILE ERROR — total reassigned\nint[] box = {0};                                 \u002F\u002F mutate object state instead\nlist.forEach(n -> box[0] += n);\n",[51,1178,1179,1197,1226,1230,1243,1251,1272],{"__ignoreMap":49},[54,1180,1181,1184,1187,1189,1191,1194],{"class":56,"line":57},[54,1182,1183],{"class":71},"int",[54,1185,1186],{"class":67}," factor ",[54,1188,72],{"class":71},[54,1190,834],{"class":684},[54,1192,1193],{"class":67},";                                  ",[54,1195,1196],{"class":60},"\u002F\u002F effectively final\n",[54,1198,1199,1201,1203,1205,1207,1210,1212,1214,1216,1218,1220,1223],{"class":56,"line":64},[54,1200,104],{"class":67},[54,1202,107],{"class":71},[54,1204,110],{"class":67},[54,1206,107],{"class":71},[54,1208,1209],{"class":67},"> f ",[54,1211,72],{"class":71},[54,1213,120],{"class":67},[54,1215,78],{"class":71},[54,1217,120],{"class":67},[54,1219,127],{"class":71},[54,1221,1222],{"class":67}," factor;   ",[54,1224,1225],{"class":60},"\u002F\u002F OK — captures the value 3\n",[54,1227,1228],{"class":56,"line":101},[54,1229,182],{"emptyLinePlaceholder":181},[54,1231,1232,1234,1237,1239,1241],{"class":56,"line":136},[54,1233,1183],{"class":71},[54,1235,1236],{"class":67}," total ",[54,1238,72],{"class":71},[54,1240,691],{"class":684},[54,1242,808],{"class":67},[54,1244,1245,1248],{"class":56,"line":178},[54,1246,1247],{"class":60},"\u002F\u002F list.forEach(n -> total += n);",[54,1249,1250],{"class":60},"                \u002F\u002F COMPILE ERROR — total reassigned\n",[54,1252,1253,1255,1258,1260,1263,1266,1269],{"class":56,"line":185},[54,1254,1183],{"class":71},[54,1256,1257],{"class":67},"[] box ",[54,1259,72],{"class":71},[54,1261,1262],{"class":67}," {",[54,1264,1265],{"class":684},"0",[54,1267,1268],{"class":67},"};                                 ",[54,1270,1271],{"class":60},"\u002F\u002F mutate object state instead\n",[54,1273,1274,1277,1279,1282,1284,1287,1289,1292,1295],{"class":56,"line":191},[54,1275,1276],{"class":67},"list.",[54,1278,520],{"class":84},[54,1280,1281],{"class":67},"(n ",[54,1283,78],{"class":71},[54,1285,1286],{"class":67}," box[",[54,1288,1265],{"class":684},[54,1290,1291],{"class":67},"] ",[54,1293,1294],{"class":71},"+=",[54,1296,1297],{"class":67}," n);\n",[15,1299,1300,1301,1304,1305,1308,1309,1312,1313,971],{},"The reason is lifetime and safety: a lambda can ",[19,1302,1303],{},"outlive"," the method that created it (be\nstored, queued, or run on another thread), and Java captures locals ",[19,1306,1307],{},"by value",". Allowing\nreassignment would create two diverging copies of the \"same\" variable. The idiomatic fix\nisn't the array trick or ",[51,1310,1311],{},"AtomicInteger"," — it's to avoid mutation and let a stream do the\nwork: ",[51,1314,1315],{},"list.stream().mapToInt(Integer::intValue).sum()",[10,1317,1319],{"id":1318},"lambdas-vs-anonymous-classes","Lambdas vs anonymous classes",[15,1321,1322,1323,1326,1327,1332,1333,1336,1337,1339],{},"Lambdas ",[39,1324,1325],{},"look"," like terser anonymous classes, but the differences run deep — and they are\nclassic interview material. The headline gotcha is ",[19,1328,1329],{},[51,1330,1331],{},"this",": inside a lambda it refers to\nthe ",[19,1334,1335],{},"enclosing instance",", because a lambda introduces no new scope. Inside an anonymous\nclass, ",[51,1338,1331],{}," is the anonymous object itself.",[416,1341,1342,1355],{},[419,1343,1344],{},[422,1345,1346,1349,1352],{},[425,1347,1348],{},"Aspect",[425,1350,1351],{},"Lambda",[425,1353,1354],{},"Anonymous class",[438,1356,1357,1375,1386,1408],{},[422,1358,1359,1363,1370],{},[443,1360,1361],{},[51,1362,1331],{},[443,1364,1365,1366,1369],{},"the ",[19,1367,1368],{},"enclosing"," instance",[443,1371,1365,1372],{},[19,1373,1374],{},"anonymous object",[422,1376,1377,1380,1383],{},[443,1378,1379],{},"Scope",[443,1381,1382],{},"shares the method's scope",[443,1384,1385],{},"introduces its own",[422,1387,1388,1391,1402],{},[443,1389,1390],{},"Compilation",[443,1392,1393,1398,1399],{},[19,1394,1395],{},[51,1396,1397],{},"invokedynamic",", no extra ",[51,1400,1401],{},".class",[443,1403,1404,1405,1407],{},"a separate ",[51,1406,1401],{}," file",[422,1409,1410,1413,1416],{},[443,1411,1412],{},"State",[443,1414,1415],{},"only the one SAM",[443,1417,1418],{},"fields and multiple methods",[44,1420,1422],{"className":46,"code":1421,"language":48,"meta":49,"style":49},"class Widget {\n  String name = \"panel\";\n  Runnable make() {\n    return () -> System.out.println(this.name); \u002F\u002F \"panel\" — Widget's this\n  }\n}\n",[51,1423,1424,1434,1446,1457,1480,1485],{"__ignoreMap":49},[54,1425,1426,1429,1432],{"class":56,"line":57},[54,1427,1428],{"class":71},"class",[54,1430,1431],{"class":84}," Widget",[54,1433,295],{"class":67},[54,1435,1436,1439,1441,1444],{"class":56,"line":64},[54,1437,1438],{"class":67},"  String name ",[54,1440,72],{"class":71},[54,1442,1443],{"class":91}," \"panel\"",[54,1445,808],{"class":67},[54,1447,1448,1451,1454],{"class":56,"line":101},[54,1449,1450],{"class":67},"  Runnable ",[54,1452,1453],{"class":84},"make",[54,1455,1456],{"class":67},"() {\n",[54,1458,1459,1462,1464,1466,1468,1470,1472,1474,1477],{"class":56,"line":136},[54,1460,1461],{"class":71},"    return",[54,1463,75],{"class":67},[54,1465,78],{"class":71},[54,1467,81],{"class":67},[54,1469,85],{"class":84},[54,1471,88],{"class":67},[54,1473,1331],{"class":684},[54,1475,1476],{"class":67},".name); ",[54,1478,1479],{"class":60},"\u002F\u002F \"panel\" — Widget's this\n",[54,1481,1482],{"class":56,"line":178},[54,1483,1484],{"class":67},"  }\n",[54,1486,1487],{"class":56,"line":185},[54,1488,362],{"class":67},[15,1490,1491,1492,1495,1496,1500,1501,1504,1505,1508],{},"Compilation differs too. An anonymous class generates a ",[51,1493,1494],{},"Widget$1.class"," at build time; a\nlambda compiles to an ",[19,1497,1498],{},[51,1499,1397],{}," instruction plus a private synthetic method, and\nthe JVM's ",[51,1502,1503],{},"LambdaMetafactory"," builds the implementation ",[19,1506,1507],{},"lazily"," at runtime. That means no\nclass-file explosion, smaller jars, and a stateless capture-free lambda can even be reused\nas a singleton.",[10,1510,1512],{"id":1511},"target-typing","Target typing",[15,1514,1515,1516,1519,1520,1523,1524,1527],{},"A lambda has ",[19,1517,1518],{},"no type of its own"," — its type is decided by the ",[19,1521,1522],{},"context"," it appears in,\ncalled the ",[19,1525,1526],{},"target type",". The compiler reads that context, finds the single abstract\nmethod, and checks the lambda's parameters and return against it.",[44,1529,1531],{"className":46,"code":1530,"language":48,"meta":49,"style":49},"Runnable       r = () -> doWork();                     \u002F\u002F target type Runnable\nCallable\u003CVoid> c = () -> { doWork(); return null; };   \u002F\u002F target type Callable\nComparator\u003CString> byLen = (a, b) -> a.length() - b.length();\n",[51,1532,1533,1553,1587],{"__ignoreMap":49},[54,1534,1535,1538,1540,1542,1544,1547,1550],{"class":56,"line":57},[54,1536,1537],{"class":67},"Runnable       r ",[54,1539,72],{"class":71},[54,1541,75],{"class":67},[54,1543,78],{"class":71},[54,1545,1546],{"class":84}," doWork",[54,1548,1549],{"class":67},"();                     ",[54,1551,1552],{"class":60},"\u002F\u002F target type Runnable\n",[54,1554,1555,1558,1561,1563,1565,1567,1569,1571,1574,1576,1578,1581,1584],{"class":56,"line":64},[54,1556,1557],{"class":67},"Callable\u003C",[54,1559,1560],{"class":71},"Void",[54,1562,145],{"class":67},[54,1564,72],{"class":71},[54,1566,75],{"class":67},[54,1568,78],{"class":71},[54,1570,208],{"class":67},[54,1572,1573],{"class":84},"doWork",[54,1575,172],{"class":67},[54,1577,211],{"class":71},[54,1579,1580],{"class":684}," null",[54,1582,1583],{"class":67},"; };   ",[54,1585,1586],{"class":60},"\u002F\u002F target type Callable\n",[54,1588,1589,1591,1593,1596,1598,1600,1602,1604,1606,1608,1610,1612,1614],{"class":56,"line":101},[54,1590,139],{"class":67},[54,1592,142],{"class":71},[54,1594,1595],{"class":67},"> byLen ",[54,1597,72],{"class":71},[54,1599,150],{"class":67},[54,1601,78],{"class":71},[54,1603,155],{"class":67},[54,1605,158],{"class":84},[54,1607,161],{"class":67},[54,1609,164],{"class":71},[54,1611,167],{"class":67},[54,1613,158],{"class":84},[54,1615,388],{"class":67},[15,1617,1618,1619,1622,1623,1626,1627,1629],{},"This is why the ",[39,1620,1621],{},"same"," arrow shape can implement different interfaces, and why a lambda\ncannot be assigned to ",[51,1624,1625],{},"var"," or to ",[51,1628,266],{}," — there's no abstract method to infer against.",[10,1631,1633],{"id":1632},"primitive-specializations","Primitive specializations",[15,1635,1636,1637,1640,1641,1644,1645,1647,1648,1650,1651,718,1653,769,1656,1659],{},"Generic interfaces only hold reference types, so ",[51,1638,1639],{},"Function\u003CInteger,Integer>"," ",[19,1642,1643],{},"autoboxes","\nevery ",[51,1646,1183],{}," into a heap ",[51,1649,107],{}," — wasteful in hot loops and streams. The primitive\nspecializations work directly on ",[51,1652,1183],{},[51,1654,1655],{},"long",[51,1657,1658],{},"double"," to eliminate that overhead.",[416,1661,1662,1671],{},[419,1663,1664],{},[422,1665,1666,1669],{},[425,1667,1668],{},"Specialization",[425,1670,433],{},[438,1672,1673,1683,1693,1703,1713],{},[422,1674,1675,1680],{},[443,1676,1677],{},[51,1678,1679],{},"IntFunction\u003CR>",[443,1681,1682],{},"int → R",[422,1684,1685,1690],{},[443,1686,1687],{},[51,1688,1689],{},"ToIntFunction\u003CT>",[443,1691,1692],{},"T → int",[422,1694,1695,1700],{},[443,1696,1697],{},[51,1698,1699],{},"IntUnaryOperator",[443,1701,1702],{},"int → int",[422,1704,1705,1710],{},[443,1706,1707],{},[51,1708,1709],{},"IntPredicate",[443,1711,1712],{},"int → boolean",[422,1714,1715,1724],{},[443,1716,1717,1720,1721],{},[51,1718,1719],{},"IntSupplier"," \u002F ",[51,1722,1723],{},"IntConsumer",[443,1725,1726],{},"() → int \u002F int → void",[44,1728,1730],{"className":46,"code":1729,"language":48,"meta":49,"style":49},"IntUnaryOperator square = x -> x * x;   \u002F\u002F no boxing anywhere\nsquare.applyAsInt(5);                    \u002F\u002F 25\n",[51,1731,1732,1753],{"__ignoreMap":49},[54,1733,1734,1737,1739,1741,1743,1745,1747,1750],{"class":56,"line":57},[54,1735,1736],{"class":67},"IntUnaryOperator square ",[54,1738,72],{"class":71},[54,1740,120],{"class":67},[54,1742,78],{"class":71},[54,1744,120],{"class":67},[54,1746,127],{"class":71},[54,1748,1749],{"class":67}," x;   ",[54,1751,1752],{"class":60},"\u002F\u002F no boxing anywhere\n",[54,1754,1755,1758,1761,1763,1765,1768],{"class":56,"line":64},[54,1756,1757],{"class":67},"square.",[54,1759,1760],{"class":84},"applyAsInt",[54,1762,88],{"class":67},[54,1764,853],{"class":684},[54,1766,1767],{"class":67},");                    ",[54,1769,1770],{"class":60},"\u002F\u002F 25\n",[15,1772,1773,718,1776,769,1779,1782,1783,1786,1787,1789],{},[51,1774,1775],{},"IntStream",[51,1777,1778],{},"LongStream",[51,1780,1781],{},"DoubleStream"," are built on these, which is exactly why\n",[51,1784,1785],{},"mapToInt(...).sum()"," outperforms mapping to boxed ",[51,1788,107],{},"s. When you control the type,\nprefer the primitive specialization in any performance-sensitive path.",[10,1791,1793],{"id":1792},"recap","Recap",[15,1795,33,1796,1799,1800,1803,1804,1806,1807,1809,1810,1814,1815,718,1817,718,1820,718,1823,1825,1826,710,1828,1830,1831,710,1833,710,1835,1837,1838,1841,1842,1845,1846,1851,1852,1856,1857,1860],{},[19,1797,1798],{},"lambda"," is anonymous-function syntax for the ",[19,1801,1802],{},"single abstract method"," of a\n",[19,1805,246],{},"; ",[51,1808,396],{}," makes that contract compiler-enforced. The\n",[19,1811,1812],{},[51,1813,413],{}," family (",[51,1816,709],{},[51,1818,1819],{},"Supplier",[51,1821,1822],{},"Consumer",[51,1824,758],{},", and their\noperator and bi-arg variants) covers almost every case, and their ",[51,1827,740],{},[51,1829,750],{},"\u002F\n",[51,1832,763],{},[51,1834,768],{},[51,1836,774],{}," defaults let you compose small pieces into big behavior. ",[19,1839,1840],{},"Method\nreferences"," in their four kinds (static, bound, unbound, constructor) trim lambdas that\njust forward arguments. Captured locals must be ",[19,1843,1844],{},"effectively final"," because lambdas\ncapture by value and can outlive their method. Finally, lambdas differ from anonymous\nclasses in ",[19,1847,1848,1849],{},"lexical ",[51,1850,1331],{},", scope, and ",[19,1853,1854],{},[51,1855,1397],{}," compilation — and the\n",[19,1858,1859],{},"primitive specializations"," exist to dodge autoboxing. Master these and the entire\nstreams API becomes readable.",[1862,1863,1864],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":49,"searchDepth":64,"depth":64,"links":1866},[1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877],{"id":12,"depth":64,"text":13},{"id":29,"depth":64,"text":30},{"id":240,"depth":64,"text":241},{"id":404,"depth":64,"text":405},{"id":725,"depth":64,"text":726},{"id":974,"depth":64,"text":975},{"id":1153,"depth":64,"text":1154},{"id":1318,"depth":64,"text":1319},{"id":1511,"depth":64,"text":1512},{"id":1632,"depth":64,"text":1633},{"id":1792,"depth":64,"text":1793},"How Java lambdas and functional interfaces actually work — syntax, the java.util.function family, composition, method references, effectively-final capture, and how lambdas differ from anonymous classes.","medium","md","Java",{},"\u002Fblog\u002Fjava-lambdas-functional-interfaces","\u002Fjava\u002Fstreams-functional\u002Flambdas-functional-interfaces",{"title":5,"description":1878},"blog\u002Fjava-lambdas-functional-interfaces","Lambdas & Functional Interfaces","Streams & Functional","streams-functional","2026-06-20","JFpJMiX5BMcO3Mr0FmCbDtyrychVZbsCGvQJDdjdO4I",1782244089437]