Video details

Solving the expression problem using Modern Java by Richard Warburton, Raoul-gabriel Urma

JavaScript
01.05.2022
English

For more info on the next Devoxx UK 👉 https://www.devoxx.co.uk
Express yourself: solving the expression problem using Modern Java
Can you believe it is Java 17 already? Another small update? Not this time, Java 9 to Java 17 has brought significant changes in how we can now write day to day Java code. Introducing, Functional Programming 2.0. In this talk, we will cover the notorious expression problem and how it can be addressed using Java’s most modern language features including switch expression, sealed classes and pattern matching. We will incrementally refactor a code written pre-Java 9, using new features and approaches whilst showing the resulting code benefits including cohesion, decoupling and readability.

Transcript

Yes, some enthusiasm. A little bit of enthusiasm. Let's try that again. How's it going? Yeah, fantastic. Hey, Richard. Richard, how much coffee have you had? A couple of cups. Not that much. Just coffee? No. Red Bull vodka? No, just coffee. Just coffee. Amazing. Good to see you, everybody. It's been a while. Absolutely. It's the first time we've given an in person talk for like two years or so. Yeah, it's great to be back. It's lovely to have people in the room. Let's talk about some Java. We'll talk about some Java. That's the point. And we've got a very interesting talk title, Express Yourself. Nothing to do with the famous football song at all, Richard. No, we're talking about the expression problem. Firstly, who are we, Raul? Who are you? Let me do yours and do mine. Right. So, Richard, your bid looks a bit different. No, I shaved. You shaved. Very nice. Very nice. I've known Richard for ten years. We met at the London Java Community Group. That's a while back, ages ago. Richard now is CTO and co founder, Option, a performance analytics startup doing great work. We've written a book together. Real world Software development in Java. You wrote another book? Yeah. Java Java Atlantis. Also Java Champion. I believe that's true. And you maintain a few Princess projects or an all round geek. Thanks, RAL. You're the CEO of Cambridge Spark. Aside from the book you wrote with us, you also wrote Modern Java in Action. Also an industrial fellow at Cambridge University, which sounds like a very fancy title. Well, if anybody is interested in the research in the computer science Department, Cambridge, feel free to connect. That's part of my role. Okay. But aside from us, let's go to a University setting just to kind of set the scene for the problem that we're talking about, because we're talking about Java code. But we're talking about kind of an academic problem that's originally from a kind of computer science background. And that's the expression problem. Right. And the expression problem is originally kind of defined by this guy, Philip Wodler. And he's basically saying that the expression problem is about defining functions and you've got data types and you've got different cases within that data type and you've got to define functions over that data type, and you've got to retain static type safety. So it's kind of three different bits of that functions, different cases of the data type and static type safety. Richard, I don't know what Philip. I'm sure he had a lot of coffee that day as well. But this definition sounds really abstract to me. Right? It doesn't make a lot of sense if you're familiar with the Explorer head emoji. That's probably how I feel right now. So it sounds like we're talking about adding new types, new operations and the types. And we want the compiler to help us navigate through those additions. So can you make it concrete for me, Richard? Okay. So let's take a more real world example. Let's imagine that you're running some kind of financial exchange and you've got some kind of data type where you're changing an order that you're putting on to that financial exchange. So different changes you might make are adding a new order. So like say I want to go and buy some shares of some particular stock, canceling an order that's on the market or doing like a replace. So atomically swapping one order with a different one in there to say change the price one way or another. Okay, so this is what Waddler means when he talks about different cases to a data type. We've got a data type changing the order and we've got different things within that change order, different cases within that change order. What about the operations, Richard? What if I want to do some validation logic? How does it work exactly? So you talk about defining functions over a data type, right? So maybe one of the functions you want to define is validating that those orders are correct. Another one that you might want to define is updating your order book to represent the different changes in orders that you get from these different data types. I love you Richard. You're a nice guy, right? Thank you. But let's say I want to add like a new case, a new type. How does it work? Do I have to implement everything all the time? Right, exactly. So you end up with this kind of matrix of different combinations here. Like we may want to add new cases to that data type. So a case example might be suspending an order that's on the market rather than canceling. So making sure it doesn't get executed for a bit. Or you might have different functions that you want to add, like say something additional with respect to checking risk or creating audit logs to meet new super boring compliance requirements that are always cropping up in these kind of industries. But the point is you've got to kind of make sure that you've got these different combinations of things. And part of the static type safety is saying that you can cover these different cases so you can't have a function that just ignores one of these things without it being deliberate. Right. So we need the compiler to help us. If we're in UK, have you ensure that you've implemented all those functions? So if you add in operations, have you made sure that you've handled all the cases? That's really what we want to get out of the compiler here. But surely Richard, we also want to do this in a way that is concise and easy for us. Right? So can you do that for me? Absolutely. What we're going to show you is a bit of a comparison between different programming styles here. Bit of a fight. New James Bond film came out recently. Did anyone watch? No Time to Die. Great movie. Quite a few people. Excellent. Fun. I enjoyed it. But we're going to show you a more exciting fight than any James Bond film, because it's a fight between different programming styles. Okay, Richard, you're going to be the villain. No, you're the villain. I'm the good guy. I'm the good guy. I'm the good guy. I've shaved. I'm definitely the good guy. That's fair. Okay, so let's look at some Java code. Right? So we've talked about the kind of the problem concept that we're looking at, and we've got some code on screen, and we're going to basically show you a kind of more traditional Oop way of solving this problem. And then we're going to show you a more functional style for solving it using the new Java features that have sort of been introduced between Java eleven and 17. So what I want to do is, you know, implement some function that takes, say, an order book and some kind of order. And we want to be able to abstract over different cases. So what we've got here is we have a new order class here. Beautiful. Verbose, delightful Java code that we all know and love. Right? As Raul says, if you're paid by the number of lines of code, Java is an amazing programming language. Right? So we've got a new order, and we've got similar things here. He's definitely a villain. Definitely. I'm the good guy. I'm the good guy. So we've got things like a cancel order that takes the original order ID to cancel, and the new order, the new different fields and stuff like that. So let's try and write some code. So firstly, we don't just want to write code that operates on a new order. Right? We want to write code on things that operate on different types of order, the different cases, as Water says. So we need to add in Java an interface to deal with that kind of problem, the change order interface. So that's going to help us introduce different cases. So those are going to be all the subtypes of this interface enabling us to switch them around. And we've got still one single method that can operate with multiple cases. That sounds great, Richard. Absolutely. And we want to be able to implement our different functions on the C and E. So that's change order. So nice. Perfect. Programming to the rescue. We love it. Lovely. Thank you very much. So if we're validating our function and we can just create a method in our change order interface, and we also want to do other things. Right? So if we validate our order, we want to be able to say take that change order and update our order book as well and have a function for that. Okay. So we've added our interface. We've added some methods on the interface. So far, so good. So what? Well, we need to go and find our other classes like new order. Do we need to interface. Absolutely. We need to make them implement the interface. And obviously we need to implement some methods. So validation might be I'm not super interested in what the different code is, what the different logic is here necessarily, but that's good. You get a compiler to help you here because we implement an interface. There's a contract that we need to handle here. There's the separations that need to be supported. So it's amazing the compiler is navigating us, helping us get to the right solution, right? Absolutely. So if we implement that interface and we don't have the methods to find over it, the compiler is checking that we don't have that. So that's good. I've got some very simple validation here, like just checking the prices positive, for example, and printing out for the update order book. Do you need to do that with the other cases, Richard? I think absolutely, we need to do that with the other cases. So we need to add it for cancel order. And I'm just going to make this return true on every occasion. And I'm just going to print out the names of the methods that we're calling so you can kind of see how the code that's being executed corresponds to those different methods. So if we run on order with the new order and with our Council order operation, and then if we just run this code, we should see it runs those different methods that are implemented within the different cases. There's also replaced, but I've not bothered implementing that just to save time in this presentation. Okay, so, Raul, Java champion, Java expert, what's your score out of ten for my solution here. What do you think, Richard? You know I love you, mate. Yeah. It's been a long, long term friendship. Yeah. Thank you. But let's ask the audience. Let's ask the audience. Who gives Richard. And let's remember what we have to hear is conscious. We're not paid by lines of code. We pay by writing maintainable code that is easy to read, easy to change. So who gives Richard a five out of ten? Raise your hand. Who gives Richard a six out of ten? Okay, who gives Richard a ten out of ten? No, one. A seven. Who gives Richard below five? Let me give you a hug, man. Thank you. So, Richard, I think it's a great attempt, right. Unfortunately, there's a couple of things that are happening here. One is like you're really coupling the data together with those operations that are not related to those cases. Right. So that's a problem then? The interface, it looks like it's an interface where we're just going to boil down a bunch of random operations. So it doesn't feel very cohesive either. So if we take our software engineering kind of like a principle, it looks like we're breaking a few things here. So I'll give you a five. I'm a nice guy, but it's not quite what we want, Richard. Okay. So this is what I like about pair programming. You get useful feedback on how to improve your code. Okay, so smart specific measurable. Absolutely. Let's solve this problem. And if you open like gang of four design patterns book, they'll tell you use the visitor pattern. Right. So how does that work? Well, we add some interface here called a change order visitor and we're going to change our change order interface so that it has a change order visitor parameter. That sounds really fancy, Richard. Yeah, it does sound funny, doesn't it? So is this visitor about? Well, it's a way that we can separate out the logic that we've put it here. We've embedded it within these data cases so we want to be able to change those things. So instead of having the logic for doing the validation of the update in those data cases, we can decouple those things and we can just say look, take the visitor and say on cancel order and pass yourself in as a parameter there. That sounds super interesting. So it's almost like you've got this visitor that can encapsulate functionality so it doesn't have to sit within each case anymore. So you've kind of decoupled that. And there's one single place that would encapsulate all the validation logic. Maybe that would be a visitor, the change order would be another visitor. Is that what you're doing here, Richard? Absolutely. So we've got another case for the on new order. And so this is going to end up being a double dispatch here, right. So our example code as opposed to putting these things in here, we're going to change that as well in a SEC. And we need to add a couple more classes here. So I'm going to do a validator class so that's going to implement our change order visitor. And then we've got these different methods. So this is beginning to see how the function and the cases kind of map together. So we've got the one for the case for the Council order, the case for the new order. And obviously the more cases we have, the more methods we would have in those things. And we've got an update class for updating the order book and that also implements the change order visitor. But that's nice. Right now we're getting the compiler to tell us these are all the cases that you need to support. Right. That's done for us. We can't really forget because it's part of the contract. Absolutely. If we missed out that method, we would get a compile error there in our class and it looks like it's also more functionally cohesive. Each visitor has all the logic regarding a specific function, whether it's validation, I've got a visitor for it, it does that. If it's updates, I've got another visitor, it does that. So we can reason about the code a bit better here because we know where everything is located as opposed to sprinkle around, maybe tens or 20s of different cases. Absolutely. And when we take our change order, we can accept our visitor. Then that goes to the specific implementation, and then that double dispatches back to the implementation within the visitor implementation there. So we've solved the coupling problem. We've still got type safety here in the sense that if we miss out one of those case combinations, our code won't compile and we pulled the code out from there. So you give me a score last time. What's the score now? Role like, you know I love you. Yes. I need to question whether you do. I generally do. But sometimes in French we say Kimbi Sha T bin. It's like if you love someone, you also got to be able to tease them really well. Okay, so that's the kind of relationship we've got. We said the villains are the ones that write a lot of boilerplate code and get charged by lines of code. Yeah. Do you agree on that? Well, if you go by James Bond films, it's more like people with beards who talk with funny accents. Either way, it sounds to me like you have solved a couple of the issues. We had function cohesion with stereotype safety. But there's a lot of code that you're writing, right? There's a lot of bulletplate code. That's just Java, mate. That's just Java. There's a lot of code. Let's see what the audience thinks about. Who gives Richard five or less for this solution? Who really enjoyed this kind of solution? Who thinks this is amazing? Come on, who's giving me a ten for the visitor? Somebody. Okay, so I feel like people are more in the 6th range now. It's not improvement, perhaps, but at the trade off of introducing a lot of bullet plate, Richard, and complexity, it's quite a conceptually hard design patterns. Right. This double dispatch mechanism, it looks a bit like a bit of magic. What happens when you control click through here, it goes through an interface that's confusing for people. Okay, Ralph, how about you show me some alternatives? What's your proposed alternative? All right, mate, time to tag you in. Stand back. Let's write some Java code. Let's maybe summarize a little bit of different approaches, Richard, before we kick off. Right? Yeah. So what we've seen here is that we've increased a lot of busy. Yeah. So we need to prove the tax. Right now, I think I'd rather be winning the Poo with the tax than without the tax, though. That's fair. That's a good point. So I guess what we're trying to achieve here is how can we get decoupling higher cohesion, but also less verbosity, less boilerplate. That's what we're going to try and figure out. So what I like with the visitor approach is that we kind of flipped the problem a little bit. So the op style is you've got a case, let's Bolt down a bunch of operations in that class. Whereas the visitors say, forget about the cases, let's bowl down the functionality itself in a single class and that handles multiple cases. So we know that gives us higher functional cohesion and decoupling. So let's keep going down this path, but maybe approach it slightly differently. So I've rewritten Richard's code using good old instance of as a way to get started. So it's kind of like a similar idea, right? So we've introduced a class that's going to capsule the operations, has a validation and handles multiple cases. But Richard, this is a lot simpler than the visitor pattern, right? I don't have to think about double dispatch. I can just do an EFL in different cases and have specific logic associated to it so much simpler than the visitor pattern. What do you think? I think, look, you know I love you, right? Okay. You know I love you. But even though this has less code, even though it's simpler, you've lost the compile time checking, right? If you delete one of those cases in the middle of your code, this is still going to compile until runtime and it's just going to return false and you're going to be dropping some kind of order update on the floor or you're not going to be validating it. You can miss that combination out. There's no static type safety. So for me, even though it's simpler, it's in many ways a worse solution than the visitor person. All right, you're right. Look, if I comment this out compiler's not really telling me. Hold on, you forgot something here. Right. But we've got more concision, so it's almost like you get one but a change of something else. So how can we get back even your concision cases? Right. Like you've got scenarios here where you're doing the cast, you're doing an install check and then a cast, which is really common in Java code, but it's still quite the boast. Right. Okay. Well, on that point I do agree there's a bit of we can't explain the same thing twice here. I'm checking that it's an insert of new order and then I have to remind the compiler that's new order. So that's a bit silly. Also, I could get it wrong. What if I cast you something completely different? So maybe you can change it a little bit. So some version of Java is it just 16 I'm losing track of when things are added. But it's in the 17, it's in Ults and probably very few people are going to use anything between eleven and 17. That's right. Let's assume you have a 17. We can kind of move out the local variable declaration here, right at the condition just to tidy things up a little bit. So cancel order. So doing like a check and then an extraction, we can kind of do two birds 1 St approach whereby once we know it's an instant cancel order, we've got the local variable here available to us. So let's do the same with a replace order like that. So this is new added between Java Eleven and Java 17 and it's now ready to use. Right. So Richard, nice. That allows me to have my code slightly more concise and also more type safe. What do you think? So it's a little bit more type safe. I think it's definitely improved on the verbosity fronts. What do people think? Who prefers this code? Put your hand up. Now, compared to the Vista code, not many people who prefers the visitor code. Mario Lexit. Mario Lexit. A lot of people prefer the Vista code. There you go. So the velocity reduction is real. Mario is a fan, but there's still the compile time checking issue. All right, mate, I'm not fully sold. So you want more concision in a way that actually is not as scary as the visit pattern. But this is not quite it just yet. Because we're still losing type safety. We don't know if there's more cases that needs to be checked. Alright. So Richard, I'm going to freestyle a little bit and make use of a new feature that was added in Java 14. I believe the switch expression. So the switch works a little bit like that, right? So we can now say we're going to switch over some variable here and then we can write some codes with some different cases. So the reason I like switch is because there is a convenient syntax which allows us to switch over multiple values, multiple enum values, or multiple cases, multiple types. So it's a concise language feature to check for different things when there are values. Right. So that's why it's quite useful. Well, it's in Java 17. Now what we can do is to do a case over types. So maybe I'll do a case over a new order and given my new order, what I want to say is actually let's just check that the price is greater than zero. Wonderful. So that's new. So we can now kind of almost get this instance of behavior but into a new, more concise language feature. So you've got two new things here, right? Like one of them is the pattern matching on the type here. And the other thing is that the switch itself is an expression previously bodies within the switch statements. It was a series of statements. You could add some braces there, you'd have to have a break or the rest of it. Here you have the different cases and they evaluate to a value and then you get an expression style switch, which is kind of what you want in this kind of case, having different cases evaluate the values. Absolutely. So let's keep going through the list. So my next one is the replace order which will check against and then voila, however, so it looks more concise. What do you think, Richard? That's much better than a bunch of infinites that we have to chain up for different cases. This is slightly more tidy, easy to read. So to me that looks grand. The only problem is when you have an interface, you've got a change order interface. You can have any class within your class path. Just come along and implement that interface. Right. It was declared public. Even if you made it to the package scope, it could be any number of cases within that package. So how do I know that you've got new cancel and replaced? How do I know that those are the only implementations of that interface? Because with the Vista pattern, the implementation of Vista pattern ensured that compiled time checking. But how would you know that with an interface? You're absolutely right, Richard. So we've got a little problem here. Interfaces are really open. Anybody can implement this change order interface, I might not know about it. So how can we tell the compiler like hold on, there's a finite list of cases that are part of this interface and I want you to check against that so we can get a benefit of type safety, something that the compiler here cannot yet support us with because it thinks change order. Potentially there's an infinite number of cases. So the good news is there's a new feature that was introduced which allows us to support bit more sophisticated domain modeling. And it's the concept of a sealed type. And a sealed type can indicate what are all the permitted cases, all the permitted subtypes. So we can go from infinite to something that's finite, and then the compiler can help us to make sure that we actually checked against a list that we are providing. So we're going to have the new order, I believe, cancel order and replace order. So let's see what we've got here. So we have to make those final, which means that we can't actually extend from the subcases because otherwise that against greater scope for many possible cases. Yeah, if you didn't make them final, you'd have to have other classes that you could have other classes elsewhere, then extend those types and then that would break the ceiling of it. But you don't have to make the final. You can seal those classes and add permits for extending them as well. And let's check it out. We now know that actually the comparison says, look, it's all working fine, let's forget the case. It's going to tell me, hold on, you haven't covered all the possible values. So we're getting the benefits of concision, but also type safety. The compiler is helping us telling us, hey, given that change order is sealed interface with those three permitted cases, have you really checked that they are all supported? I like it. I like it that's way better than the previous code you wrote. Great, thank you. You're being really kind. So what do people think in the audience? How about hands up if you hate this and prefer the visitor pattern that's nobody hands up if you prefer this approach to the visitor pattern. That's a room full of people. Bingo. Bingo. Super. So what do we have here, RAL? Like in your solution? We had expression based switch statements. That was a Java language feature change. We had sealed interfaces. That's a Java language feature. And permitting those extends and Switch itself supports type pattern. That's something that supports itself. The only thing that's not in 17 released but is a preview feature is that type checking, right? That's correct. That's still a preview feature. Yes, I've got 17 preview on my IntelliJ, but that's not the only thing. Richard, there's a scope for charging less money, right. By removing lines of code. You've talked to that as types of quite heavy. Yeah. Don't tell me that. I like my big robot Travis classes. I don't know why. What's the alternative? What's the alternative then? We've got a final class, we've got a bunch of fields, we've got a bunch of gears, we got a bunch of setters. We don't have a hash code equals method here, but you might well want to have them on value based types. And you might want to have a two string method as well. So you can see that code more easily in logging and debugging and all the rest of it. What's the alternative to writing heaps of code here? Well, this is what I'd argue it's quite a classical Java software development, right. We have those sort of classes that simply data classes, at least now because we refactored it the methods that we're living in this class, we've moved them away to get functional cohesion in a separate operations class. So here we're defining all those fields. We have to create a constructor. We have to create the getters. There are no setters here, but it's two strength potentially in equals and all that stuff. We also know that his classes, it looks like it's an immutable class, right. The final is final. All the fields are final. The fields themselves could be immutable. That's not a requirement, but that's what we've got here. So in Java 14 previews made into 16, I believe record types were added. So it's a way to create the sort of data holders, but actually without having to generate all that bullet plate code. So keep an eye for some magic. Intellij is already supporting refactoring, so I can say convert to record type. So record types a new language feature that allows us to declare new types. We indicate what's the list of fields that we're interested in, and then the constructor to get us to string and so on are available out of the box for us. So this is how it's going to look like I can get rid of all those getters. Actually, I'm going to have to do a bit of refactoring because this gather is also available actually rectangles through different syntax. So this is how it looks like. So let me refactor the others as well. Where we add continue, here we go, and then replace order. And what you'll notice here is that the list of fields is supported here at the decoration level, so we can now get rid of all of that. So if you're a fan of Lombok, I'm really sorry. Also, if you're a fan of Longbox, we're also just really sorry. We'Ll get through this together, don't worry. So instead of Price, get Price, the convention here is that it's actually the name of the field. And there you go. Same code as before. At least when we look at this method, it looks as simple. But the real benefit is that we've been able to reduce our maintenance overhead because we don't have to worry about all these constructors and get us into string that are generated. This is actually now provided as a builtin language feature. Yeah. So you can just add a new field and you don't need to go and generate all those extra boilerplate code which I know your ID will probably generate for you, but you still have to go and deal with it, or someone accidentally forgets to update the hash code method or the equals method. Suddenly things are a bit wrong at some point through some subtle change. But the incentives clear as well that we have a data holder so the compilers potentially could leverage this information to added additional performance benefits. Absolutely. So Richard, just to summarize a little bit what we've done and just kind of want to let you in into potentially something else that is coming through, which we don't quite have yet. But as you've seen through the switch expression, we can now support at least in 17 preview a case based on the type. And then we still have to manually extract the right fields. Right. So I have to say that Price, for example, on the right hand side, but there's discussion and there's a preview feature that will add record patterns which will allow us to match on a record type. And on the left hand side indicate, well, the fields that we want to extract as local variables, which will then be available on the right hand side. So enabling us to deconstruct the type and get again the benefit of type safety. And the compiler here to help us deconstruct the record type. So it's not quite available yet, but this is definitely an area that we're going to see further development. Sweet. So it sort of lets you have your cake and eat it, right? That's the idea of some of these features. Right. We get the reduced velocity from not having to deal with all that Vista pattern, but we still get the static type check and we still get the compile time safety that we want. Absolutely. So we get concision and type safety, which is what we were looking for. And who doesn't like cake. Especially a carrot cake. To be honest, that's one of my favorite so, yeah, let's just summarize a little bit, Richard. I think there are two approaches here. We're not saying that one is necessarily better than the other one. Both have their places op really convenient to add new types. You know, you've got an interface, you implement it really nice and easy. However, you get coupling twin data operations. So perhaps makes it harder to reason about functionalities, like validation, because that's not sprinkled around maybe an infinite number of cases. That's the downside. Whereas on the FP side, I mean, we added records, so that's like a nice concision reducer either way. But that's sort of another feature that sort of come from more functional programming languages, the seal classes plus the switch let you have that kind of compile time checking. But now the new methods aren't necessarily related to their types. So there's also a question there of like, would you rather read the code where the code that was related to a new order is in the new order class, or would you rather read code where the validation was in the validation class? So there's a kind of a trade off there as well. Absolutely. But I think what's exciting here is that we're getting more new language features that help with concision and also better domain modeling. And Silclass is an example of a new feature that allows us to create a close hierarchy type, something we didn't have before. So making the intent clearer to the reader of the code. So express yourself. It's a bit like the Ying and the Yang. We really believe that those two approaches complement one another and you have more tools available in your toolbox to solve a specific problem. So we believe that's a good thing. And Jab, as we know it, is starting to look sexier and sexier, Richard. Absolutely. Yeah. Like this car, like this car like a Range Rover. I think the idea of the metaphor with a Range Rover is both a vehicle with huge offroad capability and very high levels of luxury internally. So this is how Java is becoming. Like it's both got its traditional object oriented features that's had for years and years, but also adding new kind of functional programming capabilities that can be adopted and also help us achieve these goals as well. So that's pretty cool. Absolutely. So thank you very much, everybody, for coming to a talk. It's been a real pleasure. Yeah. We're going to take questions, I believe. Yeah. Does anyone have any questions? Hey, can you just shout? Sorry. So the question is how would you use Java. Doc to document the fields of the class with records in your order? That's a record. So I think Ralph was saying you can write Java Doc for the class. I don't think there's any benefit of doing that personally. The record type itself, I think the feature itself is to create simple data holders, whether you want to document the fields or not. So if you did want to add Java Doc, you can always I don't know if a better way of doing this because this feels like a workaround, but you could definitely override the method itself and define it within the class and then add Java dot to that method. But things would still be pretty verbose. Well, you would still benefit from not having to write the hash code equals two string methods, but you would still have to have extra methods for the guesses to hold your Java Doc. Yeah. Cool. Any other questions? Sorry. Records immutable. So records were added in Java 16. They are not immutable in the sense the fields themselves. So let's take the sender. Right. Maybe that was a customer object that could be mutable itself. So the fields are final so you can reassign to them. But the fields themselves can be stateful, right. So it's not like a transitive immutable type. So one way you could look at it is saying they're shallowly immutable, the records that you're producing are immutable, but they could be holders of mutable data. Just like a normal final class where with final fields you need to ensure those fields themselves are immutable. Otherwise you can always mutate their state. Yeah, cool. There's a gentleman there. Right. So the question is it will be more useful if the class were defined within that interface. If you're packaged scoping those classes, you could definitely do that within the interface, within the same file on the interface. But yeah, that's another restriction, sort of on the package scoping front. Gentleman at the back. Sorry, can you. Right, I'll rephrase the question. So I'll try and rephrase your question and for everyone's interests, make sure we understand it. So this approach here works well when we know the list of cases and there's a single operation. I think your point is, well, if you add a new operation, like an update, that's great. Another switch called the cases that's going to work. But if I add a new case then I have to go back and find all the operations that have a switch in them. Right. Because that suddenly have to introduce a new case for all these operations. So that's the trade off, which with the objectoriented approach we didn't have that because you just add the case and then the interface tells you all the operations you need to support. So it's kind of like a different side to the coin. But I think what's better here over, say the instance of case is if you added a new case, in the instance of case you have no compile time checking, whereas here you would get compile time errors on all of those switches saying, or at least if you have the preview feature switched on with the exhaustness checking saying there's a compile error here and you've missed out this new case for say suspend order or whatever the new case is. So I think it's an improvement over the instance of check there that kind of case that problem was already solved on the visitor and sort of traditional coupled op version. Sure. Yeah, I guess the point you still have to write code, right? You can't escape that if there's a new requirement but I guess the benefit is not only get consistent but the compilers will help you to locate where you need to introduce a new handle for this case. I think there's a guy on the left within the interface. I mean you could definitely have done that in Java before this change. Anyway. I've not written code to try with you can do that but I shouldn't imagine that's changed given you could do it beforehand. Yeah. The point here is you could always limit the possible subtypes within the scope of the chiropractor but I guess the point here is we might have a validation that sits in other packages. You might want to structure your project in a more maintainable way so you still need that feature but I think it's a fair point that at least if it's in one single package with single class you could have a similar behavior. I didn't quite hear that. Sorry. So you're saying you think it's good I just can't quite catch. Maybe we should discuss this afterwards because I just can't really hear everything you're saying. Sorry. That's the problem. But yeah, but thank you. We'll have a checkoff afterwards. Cool. Thank you very much. I think we're pretty much out of time here. Four minutes afterwards. Thank you everybody for coming. If you've got any other questions we can talk about them Ashia thank you very much. Bye.