You shouldn’t subclass Spark’s Join directly because it’s a case class and final by design. Instead, wrap it in a custom logical plan node or attach extra info via metadata on expressions to safely extend functionality without breaking Spark’s optimizations.