Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Objective Action
Jun 10, 2007



Regardless of the IDE you choose (I like Eclipse!) the one thing you should really internalize is that an IDE is not a text editor. Java lives and dies by a good IDE and mastering one can make all the difference.

The number of people that I've see open IntelliJ or the like and proceed to use it as a text editor that happens to have a build button is insane. Even people working in field professionally for years.

Automated refactoring: moves, extract interface, change scope of field, etc. Type hierarchies and the various semantic find/lookup tools. Automated imports, formatting, and organization. Intelligent templates and snippets.

Embrace the IDE, love the IDE, consume the IDE's flesh and make its power your own.

Adbot
ADBOT LOVES YOU

Hekk
Oct 12, 2012

'smeper fi

ada shatan posted:

I can't tell if this is a joke post...

Only kind of.

The effort answer is that I mostly use IntelliJ IDEA because I get a free professional license because I am a (admittedly old as dirt) student. The reason I pick it over the other IDEs is because the autocomplete helps me do stuff a heck of a lot faster than having to look everything up manually.

For example: I am writing a program where I need to parse the contents of a JTextField and create an integer from the results. I know how to use a try block and throw exceptions but I don't remember offhand the syntax for parsing. So I start typing in textBoxContent = Integer..and the IDE offers me a selection of autocomplete possibilities, one of which is the one I need and I continue writing code.

This being my third programming course, I know just enough to get myself into trouble and having a guiding hand to keep me on rails makes me focus more on logic than on syntax.

Wheany
Mar 17, 2006

Spinyahahahahahahahahahahahaha!

Doctor Rope

Objective Action posted:

Regardless of the IDE you choose (I like Eclipse!) the one thing you should really internalize is that an IDE is not a text editor. Java lives and dies by a good IDE and mastering one can make all the difference.

I would also like to add that this applies to all programming languages. IDEs improve working on PHP and JavaScript code or even bash scripts.

smackfu
Jun 7, 2004

I do feel like the Eclipse concept of different window layouts for different modes (like coding vs running vs debugging) was a complexity I never felt the need for.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord
Anyone have any idea why creating an S3 connection is incredibly slow for me or how to tackle reviewing the cause? It works, but only after churning for 2+ minutes attempting to connect. This is coming from an EC2 instance in the same area, so access should be relatively fast.

This is literally all I've got at this point for a reader class:
code:
public class S3Reader {

	private String bucket_name;
	private final AmazonS3 s3 = AmazonS3ClientBuilder.defaultClient();
	private Reader reader;
	
	public S3Reader(String bucket_name) {
		this.bucket_name = bucket_name;
	}
	
	public Reader read(String key_name) {
		S3Object s3obj;
		S3ObjectInputStream s3is = null;
		try {
			s3obj = s3.getObject(bucket_name, key_name);
			s3is = s3obj.getObjectContent();
		} catch (AmazonServiceException e) {
			System.err.println(e.getMessage());
			System.exit(1);
		}
		reader = new BufferedReader(new InputStreamReader(s3is));
		return reader;
	}
	
	public void closeReader() {
		try {
			reader.close();
		} catch (IOException e) {
			System.err.println(e.getMessage());
			System.exit(1);
		}
	}
}
I'm passing the reader to another method to parse CSV values, which works but also takes a while considering it's reading 3Kb of data.

Here's the execution times:
Creating reader...
Runtime: PT2M23.466S
Parsing S3...
Runtime: PT13.158S

If anyone wants to point out a better method to do this, please feel free as I'm interested to learn how to better my awful code.

Edit: this is likely caused by how the AWS credential is being generated. I’ll be manually generating and passing a temporary cred when I get back and hopefully that should resolve this.

PierreTheMime fucked around with this message at 13:16 on Apr 20, 2019

hooah
Feb 6, 2006
WTF?
I'm kind of feeling like an idiot here. I'm trying to chain a couple of constructors together and it's just not working as expected. Here's a toy example:

Java code:
public class Foo {
    private String valueA;
    private String valueB;
    private String valueC;
    private String valueD;

    public Foo() {}
    
    public Foo(String c, String a) {
        this(a, null, c, null);
    }

    public Foo(String a, String b, String c, String d) {
        this.valueA = a;
        this.valueB = b;
        this.valueC = c;
        this.valueD = d;
    }
}
Stepping through with the debugger seems to go fine - in the four-arg constructor, I see the expected values for a and c. However, back in the code that called the two-arg constructor, value c is null. What the hell am I missing?

Edit: never mind, copy pasta strikes again. The last line in my four-arg constructor was actually
pre:
this.valueC = d
:doh:

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
What IDE are you using? If you turned on warnings, you would probably see it telling you that the first time you set valueC is being immediately overwritten by the erroneous line after it.

hooah
Feb 6, 2006
WTF?

Jabor posted:

What IDE are you using? If you turned on warnings, you would probably see it telling you that the first time you set valueC is being immediately overwritten by the erroneous line after it.

IntelliJ. Between it and our Sonar plugin I often overlook the warnings because our Sonar profile is overly strict.

TheresaJayne
Jul 1, 2011

Ola posted:

I regret not having the guts to simply post this. But it doesn't matter, really. Every IDE will tell you you've written something wrong, no IDE will tell you've made something poo poo.

Intellij, Eclipse, JavaEd, vim and Netbeans are all bad only Notepad++ is worth using EVER

Joke..... I prefer Intellij Idea though

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord
Is there a decent/accepted way to write to an output stream for an AWS S3 object? My current code using simple Files reads each line from an InputStream, transforms the values found, and passes it to a BufferedWriter, which works just fine. There doesn't appear to be an equivalent to S3 since it's a HTTP request, and I'd be interested to see if anyone else has determined the best course for this type of action.

VikingofRock
Aug 24, 2008




I have two different classes with the same name that I'd like to use in the same file, and they both have very long full path names. What's the best thing to do here? Optimally I'd like to do something like


Java code:

import some.very.long.package.name.bar.Foo
import some.very.long.package.name.baz.Foo

// Refer to the two different Foos as bar.Foo and baz.Foo in the rest of the code, instead of needing the full name

but as far as I can tell that's not a thing in Java, nor is type aliasing.

VikingofRock fucked around with this message at 07:07 on Apr 25, 2019

CPColin
Sep 9, 2003

Big ol' smile.

VikingofRock posted:

but as far as I can tell that's not a thing in Java, nor is type aliasing.

Correct. You're stuck.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord
Alright, I've got my S3 code working as intended, now onto performance improvements. What would be the best method for threading upload requests and accumulating the PartETag results into my list, making sure all threads are completed before performing a final step? I'm still in the shallow-end of coding and need more experience with threads.

Here's what I've got working now, but if I can continue iterating over rows and making new submissions while I wait for the 100~MB files to upload it would probably be worthwhile.

Java code:
public void writeLine(String line) {
	lineStore.add(line);
	bytesRead += line.getBytes().length;
	if (bytesRead > partSize) {
		writeDataToS3Object();
	}
}

private void writeDataToS3Object() {
	//Flatten line array and create InputStream of value
	String lineStoreString = String.join("\r\n", lineStore) + "\r\n";
	System.out.println("Array size: " + lineStoreString.length());
	InputStream is = new ByteArrayInputStream(lineStoreString.getBytes());
		
	//Create object metadata
	ObjectMetadata md = new ObjectMetadata();
	md.setContentLength(lineStoreString.length());
	md.setContentType("text/html");
		
	//Create part upload request and process result
	UploadPartRequest uploadRequest = new UploadPartRequest().withBucketName(bucketName).withInputStream(is).withKey(targetKeyName).withPartSize(lineStoreString.length()).withObjectMetadata(md).withPartNumber(partNum).withUploadId(initResponse.getUploadId());
	UploadPartResult uploadResult = s3.uploadPart(uploadRequest);
	partETags.add(uploadResult.getPartETag());
        
	//Wipe current String array and size value
	lineStore.clear();
	bytesRead = 0;
	partNum++;
}
Also feel free to tell me how awful this is, because I'm sure it could be better and I appreciate critiques.

Volguus
Mar 3, 2009
Take a look at java.util.concurrent.CompletionService and an implementation ExecutorCompletionService . This should do exactly what you're looking for.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord

Volguus posted:

Take a look at java.util.concurrent.CompletionService and an implementation ExecutorCompletionService . This should do exactly what you're looking for.

Thanks, this seems to work fine. Is just calling .take() for every call made in a for loop after all submissions have been made a decent way to manage retrieving results? That's what I'm doing now and it's working but I was curious if there was a more elegant solution.

Volguus
Mar 3, 2009

PierreTheMime posted:

Thanks, this seems to work fine. Is just calling .take() for every call made in a for loop after all submissions have been made a decent way to manage retrieving results? That's what I'm doing now and it's working but I was curious if there was a more elegant solution.

Yes. Well, I presume you do something like this:
code:
CompletionService<Result> ecs= ExecutorCompletionService<Result>(e);
for(int i=0;i<10;++i) {
  ecs.submit(runnable);
}

//and after you've submitted all jobs for execution, take results as they come in

for(int i=0;i<10;++i) {
 Result res = ecs.take();
//do whatever with res
}

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord

Volguus posted:

Yes. Well, I presume you do something like this:
code:
CompletionService<Result> ecs= ExecutorCompletionService<Result>(e);
for(int i=0;i<10;++i) {
  ecs.submit(runnable);
}

//and after you've submitted all jobs for execution, take results as they come in

for(int i=0;i<10;++i) {
 Result res = ecs.take();
//do whatever with res
}

Yeah. I’m continuing to accrue Strings into an array and flatten them once they reach a limit and write the result to an object part, so I have it pulling values off the stream and tossing them to asynchronous threads once they’re balled up into S3 upload request objects. Once the stream is empty, I’m collecting all the results and tossing those into a “complete upload” request call by just looping over the number I’ve submitted. Works, just making sure I’m not making newbie mistakes.

Arcsech
Aug 5, 2008

VikingofRock posted:

I have two different classes with the same name that I'd like to use in the same file, and they both have very long full path names. What's the best thing to do here? Optimally I'd like to do something like


Java code:
import some.very.long.package.name.bar.Foo
import some.very.long.package.name.baz.Foo

// Refer to the two different Foos as bar.Foo and baz.Foo in the rest of the code, instead of needing the full name
but as far as I can tell that's not a thing in Java, nor is type aliasing.

This is one of the reasons everyone uses an IDE for Java. I can't remember the last time I actually wrote (or even modified) an import statement like that by hand.

VikingofRock
Aug 24, 2008




Arcsech posted:

This is one of the reasons everyone uses an IDE for Java. I can't remember the last time I actually wrote (or even modified) an import statement like that by hand.

I'm using IntelliJ, and you are right: I only have to write that type by hand when autocomplete decides it doesn't want to play ball for whatever reason, which is relatively rare and thus not a big deal. But even discounting the extra typing, I think the extra line noise hurts readability, which is why I was looking for a solution.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord
Anyone have a preference for a REST framework? I’m assuming some JAX-RS flavor, but I’d love to know if any of them are the better option before I start.

ChickenWing
Jul 22, 2010

:v:

PierreTheMime posted:

Anyone have a preference for a REST framework? I’m assuming some JAX-RS flavor, but I’d love to know if any of them are the better option before I start.

I really like the Spring ecosystem, although it can definitely be on the heavier side of things. Getting started with Spring Boot makes everything really easy (bootRun > build+deploy) and gives you a ton of easy autoconfiguration, plus the extensibility of spring cloud, etc.

e: also implementing OpenAPI is good and will make your consumers happy

Volguus
Mar 3, 2009
Endorsing Spring (with or without Spring Boot) as well.

Objective Action
Jun 10, 2007



I will third Spring Boot as probably the most robust and easiest to get started with. If you want to play out on the edge you might try Quarkus as long as you don't want mind being stuck at Java 8.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord
Thanks all. Spring Boot was indeed simple to setup and is very needs-suiting for the moment.

Rubellavator
Aug 16, 2007

What's a good resource to learning all the stream api and other goodies added in java 8? Most of our codebase was written before java 8 was a thing and one of our new developers has taken it upon himself to refactor a huge chunk of the code using that stuff. Frankly I find it all unintuitive and confusing as hell to read, but I dont want to be that old man programmer just yet.

CPColin
Sep 9, 2003

Big ol' smile.
Don't forget that it's 100% possible the person doing the refactoring is writing hot garbage. If it's not obvious what a big chain of stream operations is doing, it's poo poo and either needs to be simplified or thoroughly annotated with comments.

RandomBlue
Dec 30, 2012

hay guys!


Biscuit Hider

Rubellavator posted:

What's a good resource to learning all the stream api and other goodies added in java 8? Most of our codebase was written before java 8 was a thing and one of our new developers has taken it upon himself to refactor a huge chunk of the code using that stuff. Frankly I find it all unintuitive and confusing as hell to read, but I dont want to be that old man programmer just yet.

https://www.baeldung.com/java-8-streams

I'm not a fan of streams either. They're more difficult to read, meaning a higher cost of maintenance, and performance tests I've seen say they're slower than traditional methods in most cases. Still, it's good to learn regardless. Especially when you work with a streams happy dev.

CPColin
Sep 9, 2003

Big ol' smile.
A good tactic could be to have the new dev explain their thought process and show how they arrived at the piece of code you're looking at. Bonus if the dev starts to realize why certain constructs are confusing. Call it a Reverse Rubber Duck.

Rubellavator
Aug 16, 2007

CPColin posted:

Don't forget that it's 100% possible the person doing the refactoring is writing hot garbage. If it's not obvious what a big chain of stream operations is doing, it's poo poo and either needs to be simplified or thoroughly annotated with comments.

Yeah I'm aware that may be the case, but I feel like I need to do something of a crash course before I pass judgment on that.

Hughlander
May 11, 2005

CPColin posted:

A good tactic could be to have the new dev explain their thought process and show how they arrived at the piece of code you're looking at. Bonus if the dev starts to realize why certain constructs are confusing. Call it a Reverse Rubber Duck.

Extra bonus if they have to stop about what the code does while proclaiming their code is self documenting!

Objective Action
Jun 10, 2007



I'll be the dissenter and say I like both Streams and the Flow API in 9+. I had to learn a lot of the basic concepts before that anyway working with Spark for data processing tasks at scale.

Couple of hard learned points:

1)
It is not appropriate to replace every loop or iterator with a stream. Streams and Flows (or Fluxes if you are on Java 8 and using Reactor) have overhead not associated with simple loops and if you are doing only very small computations you can actually spend more time setting up and tearing down things in the background than you do on the compute. This is why you will see people post about benchmarks a lot as well. Think of Streams as basically a ThreadPool or Executor implementation with a built in message passing subsystem and some pretty smart compute graph optimization schemes hiding in the background adjudicating exactly how much actually needs to get done to satisfy a request. The one upshot of using a stream where it might not gain you an immediate benefit is that, as the underlying implementations get better, old code might get a little bit faster the next time it gets recompiled the same way you typically get speed benefits using matrix math libraries when someone figures out an optimization.

Bad:
code:
stream.map(i -> new X(i.field)).collect(...)
Better:
code:
stream.map(bunch of matrix operations).flatMap(network operation on the result).filter(...).collect(...)
It is also silly to replace simple array indexed loops with range, or worse, zip streams in almost every case; Its more complicated for usually no reason at all.

2) By the same token it is not appropriate to go hog wild replacing objects, or even methods, with lambdas all willy-nilly in every case. Its pretty drat easy to slip back into imperative programming without realizing you are doing it.

Bad:
code:
class Foo() {
	int x = stream.map(v -> {
		Five hundred lines of code here
	}).zipWith(val1...{
		A thousand more lines of code
	});
}
Better:
code:
ResultType validatedFoo = stream.map(v -> getFooFromBar(v)).filter(ValidatedFoo::isValid)...
3)
Streams and flows are lazy by default and try to be immutable where they can get away with it too. You need to call a terminal operation to trigger anything actually being done. A side effect of the implementation is more stack traces and log messages can get swallowed in closures than you might expect going in. The immutable part can creep up on you in some interesting and sometimes subtle places like the differences behind the scenes for .collect and .reduce.

4)
Streams and flows only really shine when your code is a) parallelizable and b) results in an asynchronous callback/event notification or may actually never terminate for the life of the program. This is great because you can make your code run in parallel by just popping in 'existingStream.parallel().{rest of the ops}' but the caveat is that if you, today, threw that same code in a threadpool and it misbehaved you aren't magically going to be in any less trouble in a stream. You can get a fair amount done with traditional control flows that you might have done with an executor in 7 but some subtle architectural difficulties can pop up if you try to cram code that might not initially look tightly coupled into a flow and suddenly start hitting halting and communication problems you didn't know you had.

I will say that the one other place they are quite nice as a default option is the I/O stream enhancements, particularly for files, they made in 8 which have basically replaced 99% of my use cases for those modules.

Objective Action fucked around with this message at 04:42 on May 9, 2019

RandomBlue
Dec 30, 2012

hay guys!


Biscuit Hider

Objective Action posted:

4)
Streams and flows only really shine when your code is a) parallelizable and b) results in an asynchronous callback/event notification or may actually never terminate for the life of the program. This is great because you can make your code run in parallel by just popping in 'existingStream.parallel().{rest of the ops}' but the caveat is that if you, today, threw that same code in a threadpool and it misbehaved you aren't magically going to be in any less trouble in a stream. You can get a fair amount done with traditional control flows that you might have done with an executor in 7 but some subtle architectural difficulties can pop up if you try to cram code that might not initially look tightly coupled into a flow and suddenly start hitting halting and communication problems you didn't know you had.

I will say that the one other place they are quite nice as a default option is the I/O stream enhancements, particularly for files, they made in 8 which have basically replaced 99% of my use cases for those modules.

Basically the benchmarks I've seen have shown that the only real place streams excel is in easily parallelized operations with a lot of data. Which is basically what you're saying here. Outside of that (and perhaps file I/O) they're both slower and more difficult to read and maintain than basic loops. I've seem a lot of streams code and none of it has fallen into either category. In my experience Java devs who love streams are mostly in love with "single line of code" solutions they consider elegant or clever with no consideration for either performance or maintainability. I agree that there are good places to use it but the vast majority of places I've seen it used in are to replace simple loops regardless of the amount of data or operations taking place.

I've been programming since the early 90's in Turbo Pascal, C, C++, Delphi, VB, C#, Java, JavaScript, Apex Code, SQL & PL/SQL, shell scripts etc... and generally have no problem looking at code in any language and understanding exactly what's going on and streams have been probably the first thing that that's made me have to research the code I'm reading. I've learned and can parse that poo poo fine now but it adds another barrier to entry for junior devs and it's hard enough finding junior devs that understand basic logic, let alone 5 operations chained on a single line with specialized knowledge required for things like map() vs. flatMap().

RandomBlue fucked around with this message at 06:32 on May 9, 2019

Ither
Jan 30, 2010

How much overhead do streams have? Unless one is on a really constrained system, I can't imagine it making or breaking things.

I like streams. They were hard to grok at first, but by using them more and reading the documentation & online posts, I gained a better understanding of them.

I agree that they're appropriate in all places.

hooah
Feb 6, 2006
WTF?
Speaking of streams, I've got a weird thing happening with some code I refactored slightly (changed from a String representation of a number to an Integer). Here's the gist of it:

Java code:
public int getAnswer(String answerText) {
    List<foo> fooList = someOtherThing.getList();
    return fooList.stream()
        .filter(foo -> answerText.equals(foo.getText()))
        .findFirst()
        .orElse(new Foo())
        .getAnswerNumber()
}
There was an existing test that passed null to getAnswer, but now we're seeing a NullPointerException at the orElse(). However, if I put everything in the chain up to and including findFirst() into an intermediate variable baz, I can then evaluate baz.orElse(new Foo()) in the debugger, and it returns a new Foo as one would expect. What the hell is going on here?

Imazul
Sep 3, 2006

This was actually a lot more bearable than most of you made it out to be.

hooah posted:

Speaking of streams, I've got a weird thing happening with some code I refactored slightly (changed from a String representation of a number to an Integer). Here's the gist of it:

Java code:
public int getAnswer(String answerText) {
    List<foo> fooList = someOtherThing.getList();
    return fooList.stream()
        .filter(foo -> answerText.equals(foo.getText()))
        .findFirst()
        .orElse(new Foo())
        .getAnswerNumber()
}
There was an existing test that passed null to getAnswer, but now we're seeing a NullPointerException at the orElse(). However, if I put everything in the chain up to and including findFirst() into an intermediate variable baz, I can then evaluate baz.orElse(new Foo()) in the debugger, and it returns a new Foo as one would expect. What the hell is going on here?
findFirst return an Optional<foo> and it will throw null pointer when it is null because it cannot tell the difference between finding null or finding nothing.

That's a pain in the rear end to fix in streams and will make your simple code unreadable but you can google Optional::ofNullable to learn about it.

Ola
Jul 19, 2004

This is probably an old and tired topic, but I'm trying to wrap my head around null handling, hope I can get some good tips. So unhandled NullPointerExceptions are bad, that's fine. I've worked a little bit in a university project with Optionals and I felt that while it served as a post-it note that it might be null so you unwrapped it carefully, I had to do a lot more boilerplate, wrapping and unwrapping, instead of just remembering to null-check at some suitable point.

I'm getting into C# soon, I like x ?? y, but I don't if it magically alleviates the problems of 1) remembering to null check and 2) reducing boilerplate in null handling.

Did I do Optionals wrong? (it might have been a Maybe class come to think of it) Is there some pattern which is vastly better than others or is it just horses for courses? Seems to me that checking for an invalid state is pretty fundamental to a lot of human activity for completely fair reasons (Before I drive from the cargo terminal, did my truck get loaded? Before I go to the airport, is my passport with me?) so it's pretty odd that it seems to be shunned more than embraced in programming languages.

hooah
Feb 6, 2006
WTF?

Imazul posted:

findFirst return an Optional<foo> and it will throw null pointer when it is null because it cannot tell the difference between finding null or finding nothing.

That's a pain in the rear end to fix in streams and will make your simple code unreadable but you can google Optional::ofNullable to learn about it.

So it'd be better to just drop the stream usage here? The list is never going to be very big, so performance isn't really a concern either way.

CPColin
Sep 9, 2003

Big ol' smile.

Imazul posted:

findFirst return an Optional<foo> and it will throw null pointer when it is null because it cannot tell the difference between finding null or finding nothing.

That's a pain in the rear end to fix in streams and will make your simple code unreadable but you can google Optional::ofNullable to learn about it.

That filter operation can't pass any nulls to the findFirst operation though. Or am I missing something?

RandomBlue
Dec 30, 2012

hay guys!


Biscuit Hider

Ola posted:

This is probably an old and tired topic, but I'm trying to wrap my head around null handling, hope I can get some good tips. So unhandled NullPointerExceptions are bad, that's fine. I've worked a little bit in a university project with Optionals and I felt that while it served as a post-it note that it might be null so you unwrapped it carefully, I had to do a lot more boilerplate, wrapping and unwrapping, instead of just remembering to null-check at some suitable point.

I'm getting into C# soon, I like x ?? y, but I don't if it magically alleviates the problems of 1) remembering to null check and 2) reducing boilerplate in null handling.

Did I do Optionals wrong? (it might have been a Maybe class come to think of it) Is there some pattern which is vastly better than others or is it just horses for courses? Seems to me that checking for an invalid state is pretty fundamental to a lot of human activity for completely fair reasons (Before I drive from the cargo terminal, did my truck get loaded? Before I go to the airport, is my passport with me?) so it's pretty odd that it seems to be shunned more than embraced in programming languages.

Your code should generally break down into 50% tests, 40% null checks/handling and 10% logic.

Adbot
ADBOT LOVES YOU

Imazul
Sep 3, 2006

This was actually a lot more bearable than most of you made it out to be.

CPColin posted:

That filter operation can't pass any nulls to the findFirst operation though. Or am I missing something?

You are right, it should be throwing the NPE in the filter.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply