-3

Some context so that this question actually makes some sense.

I have a trait from which I extend a case class and an object. I have a method that pattern matches to decide which to pull based on given conditions. When I pattern match and get the case class, everything works fine. But when I pattern match and get the object, an error is thrown.

Within the trait, I have methods that call the DB. And I also have vals that need overriding.

The curious thing is the error that is thrown when it the object is instantiated, is a DB specific error

java.sql.SQLInvalidAuthorizationSpecException: (conn=41) Transaction characteristics can't be changed while a transaction is in progress

Which led me on wild goose chase to hunt down whether the code was opening up another session unknowingly etc. But having checked my code, I knew I was passing around the one DB connection I had started and there wasn't any strange behaviour.

I figured, since my case class instance works, I wanted to see if converting my object into a case class would change anything.

Having converted my object to a case class, I then got an NPE error thrown which pointed me directly to the errant line of code that was causing problems. I then changed my vals to def in the trait and everything works fine.

So my questions is: Why is it that when I converted the object to the case class, it then threw up the error that was the true culprit. Which is why I asked the question: how is a case class vs object instantiated? Because I suspect this was the cause of the misleading error being thrown.

Feel free to correct my assumption if it is wrong.

trashbear
  • 109
  • 5
  • 3
    Without seeing some code is hard to know what happened but a couple of advices. Never use `vals` in traits unless they are concrete and `final` or you use them for dependant typing. Second, it is weird to mix something like plain data _(an **ADT** aka `sealed trait` + `case classes / objects`)_ with computation, like a dB connection and queries. – Luis Miguel Mejía Suárez Feb 02 '21 at 12:10
  • @LuisMiguelMejíaSuárez thanks for the tip, I do realize after I changed my vals to a def that I should be more mindful of that. The trait started out as an object where the val was simply a String but it later changed when I had more subclasses to the mix. I wanted to use pattern matching to help me pull the relevant object as each object had different queries to pull the data. They shared many of the same characteristics but their queries were different. I can't share the code as it is not my own project, appreciate the feedback. – trashbear Feb 02 '21 at 13:47
  • The compiler flag `-Xcheckinit` may help you track this kind of errors, ideally, you should separate things like data and computation, so it makes sense to use pattern matching on an **ADT** to determine which query to execute but the thing executing the query should not be the **ADT**. – Luis Miguel Mejía Suárez Feb 02 '21 at 13:59
  • @LuisMiguelMejíaSuárez thanks for the pointer for the compiler flag, I'll check it out. I thought it would be convenient for the ADT to house their own queries but with the errors I encountered, I see now that separating the two would have avoided this situation in the first place. Thanks. – trashbear Feb 02 '21 at 14:17

1 Answers1

3

Here is the crucial line:

I then changed my vals to def in the trait and everything works fine.

The problem was almost certainly caused by an abstract val in the trait being used in the constructor before it had been initialised by the subclass.

As a general rule, abstract values in a trait should always be def.

Tim
  • 26,753
  • 2
  • 16
  • 29
  • Thanks Tim, I do realize after I corrected my code that I should have been using def instead of val. The question I'm trying to get to the bottom of is why the errors being thrown when I extended the trait as an object vs as a case class were different. Your answer where you mentioned the constructor calling on an abstract val before it was initialized by the subclass gave me a clue. I've been trying to find the documentation that can help me understand this better, if you have any pointers, it would be appreciated. – trashbear Feb 02 '21 at 13:42
  • 2
    https://docs.scala-lang.org/tutorials/FAQ/index.html#why-is-my-abstract-or-overridden-val-null – Seth Tisue Feb 03 '21 at 00:39
  • @SethTisue that was really helpful - thanks! – trashbear Feb 03 '21 at 02:37