diff --git a/jvm.cabal b/jvm.cabal
--- a/jvm.cabal
+++ b/jvm.cabal
@@ -1,5 +1,5 @@
 name:                jvm
-version:             0.3.0
+version:             0.4.0
 synopsis:            Call JVM methods from Haskell.
 description:         Please see README.md.
 homepage:            http://github.com/tweag/inline-java/tree/master/jvm#readme
@@ -23,14 +23,14 @@
   exposed-modules:
     Language.Java
   build-depends:
-    base >= 4.7 && < 5,
+    base >=4.7 && <5,
     bytestring >=0.10,
-    constraints >= 0.8,
-    choice >= 0.1,
+    constraints >=0.8,
+    choice >=0.1,
     distributed-closure >=0.3,
-    exceptions >= 0.8,
-    jni >= 0.4.0,
-    singletons >= 2.0,
+    exceptions >=0.8,
+    jni >=0.4.0 && <0.6,
+    singletons >=2.0,
     text >=1.2,
     vector >=0.11
   default-language: Haskell2010
@@ -58,9 +58,9 @@
   main-is: Main.hs
   hs-source-dirs: benchmarks
   build-depends:
-    base >= 4.8 && < 5,
+    base >=4.8 && <5,
     criterion,
-    deepseq >= 1.4.2,
+    deepseq >=1.4.2,
     jni,
     jvm,
     singletons
diff --git a/src/Language/Java.hs b/src/Language/Java.hs
--- a/src/Language/Java.hs
+++ b/src/Language/Java.hs
@@ -7,12 +7,13 @@
 --
 -- @
 -- {&#45;\# LANGUAGE DataKinds \#&#45;}
+-- {&#45;\# LANGUAGE DeriveAnyClass \#&#45;}
 -- module Object where
 --
 -- import Language.Java as J
 --
 -- newtype Object = Object ('J' (''Class' "java.lang.Object"))
--- instance 'Coercible' Object
+--   deriving (J.Coercible, J.Interpretation, J.Reify, J.Reflect)
 --
 -- clone :: Object -> IO Object
 -- clone obj = J.'call' obj "clone" []
@@ -26,8 +27,8 @@
 -- To call Java methods using quasiquoted Java syntax instead, see
 -- "Language.Java.Inline".
 --
--- __NOTE 1:__ To use any function in this module, you'll need an initialized JVM in the
--- current process, using 'withJVM' or otherwise.
+-- __NOTE 1:__ To use any function in this module, you'll need an initialized
+-- JVM in the current process, using 'withJVM' or otherwise.
 --
 -- __NOTE 2:__ Functions in this module memoize (cache) any implicitly performed
 -- class and method lookups, for performance. This memoization is safe only when
@@ -41,12 +42,12 @@
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE UndecidableInstances #-}
 
 module Language.Java
   ( module Foreign.JNI.Types
@@ -73,9 +74,9 @@
   , jvalue
   , jobject
   -- * Conversions
+  , Interpretation(..)
   , Reify(..)
   , Reflect(..)
-  , Interp
   -- * Re-exports
   , sing
   ) where
@@ -117,7 +118,7 @@
 data Pop a where
   PopValue :: a -> Pop a
   PopObject
-    :: (Coercible a ty, Coerce.Coercible a (J ty), IsReferenceType ty)
+    :: (ty ~ Ty a, Coercible a, Coerce.Coercible a (J ty), IsReferenceType ty)
     => a
     -> Pop a
 
@@ -145,7 +146,7 @@
 
 -- | Pop a frame and return a JVM object.
 popWithObject
-  :: (Coercible a ty, Coerce.Coercible a (J ty), IsReferenceType ty, Monad m)
+  :: (ty ~ Ty a, Coercible a, Coerce.Coercible a (J ty), IsReferenceType ty, Monad m)
   => a
   -> m (Pop a)
 popWithObject x = return (PopObject x)
@@ -165,26 +166,28 @@
 -- | Tag data types that can be coerced in O(1) time without copy to a Java
 -- object or primitive type (i.e. have the same representation) by declaring an
 -- instance of this type class for that data type.
-class SingI ty => Coercible a (ty :: JType) | a -> ty where
+class SingI (Ty a) => Coercible a where
+  type Ty a :: JType
   coerce :: a -> JValue
   unsafeUncoerce :: JValue -> a
 
   default coerce
-    :: Coerce.Coercible a (J ty)
+    :: Coerce.Coercible a (J (Ty a))
     => a
     -> JValue
-  coerce x = JObject (Coerce.coerce x :: J ty)
+  coerce x = JObject (Coerce.coerce x :: J (Ty a))
 
   default unsafeUncoerce
-    :: Coerce.Coercible (J ty) a
+    :: Coerce.Coercible (J (Ty a)) a
     => JValue
     -> a
-  unsafeUncoerce (JObject obj) = Coerce.coerce (unsafeCast obj :: J ty)
+  unsafeUncoerce (JObject obj) = Coerce.coerce (unsafeCast obj :: J (Ty a))
   unsafeUncoerce _ =
       error "Cannot unsafeUncoerce: object expected but value of primitive type found."
 
 -- | The identity instance.
-instance SingI ty => Coercible (J ty) ty
+instance SingI ty => Coercible (J ty) where
+  type Ty (J ty) = ty
 
 -- | A JNI call may cause a (Java) exception to be raised. This module raises it
 -- as a Haskell exception wrapping the Java exception.
@@ -202,51 +205,65 @@
 withTypeRep :: Typeable a => (TypeRep -> a) -> a
 withTypeRep f = let x = f (typeOf x) in x
 
-instance Coercible Bool ('Prim "boolean") where
+instance Coercible Bool where
+  type Ty Bool = 'Prim "boolean"
   coerce x = JBoolean (fromIntegral (fromEnum x))
   unsafeUncoerce (JBoolean x) = toEnum (fromIntegral x)
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible CChar ('Prim "byte") where
+instance Coercible CChar where
+  type Ty CChar = 'Prim "byte"
   coerce = JByte
   unsafeUncoerce (JByte x) = x
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible Char ('Prim "char") where
+instance Coercible Char where
+  type Ty Char = 'Prim "char"
   coerce x = JChar (fromIntegral (ord x))
   unsafeUncoerce (JChar x) = chr (fromIntegral x)
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible Word16 ('Prim "char") where
+instance Coercible Word16 where
+  type Ty Word16 = 'Prim "char"
   coerce = JChar
   unsafeUncoerce (JChar x) = x
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible Int16 ('Prim "short") where
+instance Coercible Int16 where
+  type Ty Int16 = 'Prim "short"
   coerce = JShort
   unsafeUncoerce (JShort x) = x
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible Int32 ('Prim "int") where
+instance Coercible Int32 where
+  type Ty Int32 = 'Prim "int"
   coerce = JInt
   unsafeUncoerce (JInt x) = x
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible Int64 ('Prim "long") where
+instance Coercible Int64 where
+  type Ty Int64 = 'Prim "long"
   coerce = JLong
   unsafeUncoerce (JLong x) = x
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible Float ('Prim "float") where
+instance Coercible Float where
+  type Ty Float = 'Prim "float"
   coerce = JFloat
   unsafeUncoerce (JFloat x) = x
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible Double ('Prim "double") where
+instance Coercible Double where
+  type Ty Double = 'Prim "double"
   coerce = JDouble
   unsafeUncoerce (JDouble x) = x
   unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)
-instance Coercible () 'Void where
+instance Coercible () where
+  type Ty () = 'Void
   coerce = error "Void value undefined."
   unsafeUncoerce _ = ()
-instance Coercible (Choice.Choice a) ('Prim "boolean") where
+instance Coercible (Choice.Choice a) where
+  type Ty (Choice.Choice a) = 'Prim "boolean"
   coerce = coerce . Choice.toBool
   unsafeUncoerce = Choice.fromBool . unsafeUncoerce
 
 -- | Get the Java class of an object or anything 'Coercible' to one.
-classOf :: forall a sym. (Coercible a ('Class sym), KnownSymbol sym) => a -> JNI.String
+classOf
+  :: forall a sym. (Ty a ~ 'Class sym, Coercible a, KnownSymbol sym)
+  => a
+  -> JNI.String
 classOf x = JNI.fromChars (symbolVal (Proxy :: Proxy sym)) `const` coerce x
 
 -- | Creates a new instance of the class whose name is resolved from the return
@@ -258,8 +275,9 @@
 -- @
 new
   :: forall a sym.
-     ( Coerce.Coercible a (J ('Class sym))
-     , Coercible a ('Class sym)
+     ( Ty a ~ 'Class sym
+     , Coerce.Coercible a (J ('Class sym))
+     , Coercible a
      )
   => [JValue]
   -> IO a
@@ -333,7 +351,7 @@
 -- appropriately on the class instance and/or on the arguments to invoke the
 -- right method.
 call
-  :: forall a b ty1 ty2. (IsReferenceType ty1, Coercible a ty1, Coercible b ty2, Coerce.Coercible a (J ty1))
+  :: forall a b ty1 ty2. (ty1 ~ Ty a, ty2 ~ Ty b, IsReferenceType ty1, Coercible a, Coercible b, Coerce.Coercible a (J ty1))
   => a -- ^ Any object or value 'Coercible' to one
   -> JNI.String -- ^ Method name
   -> [JValue] -- ^ Arguments
@@ -365,7 +383,7 @@
 
 -- | Same as 'call', but for static methods.
 callStatic
-  :: forall a ty. Coercible a ty
+  :: forall a ty. (ty ~ Ty a, Coercible a)
   => JNI.String -- ^ Class name
   -> JNI.String -- ^ Method name
   -> [JValue] -- ^ Arguments
@@ -398,16 +416,18 @@
 
 -- | Get a static field.
 getStaticField
-  :: forall a ty. Coercible a ty
+  :: forall a ty. (ty ~ Ty a, Coercible a)
   => JNI.String -- ^ Class name
   -> JNI.String -- ^ Static field name
   -> IO a
 {-# INLINE getStaticField #-}
 getStaticField cname fname = do
   let retsing = sing :: Sing ty
-      klass = unsafeDupablePerformIO $
-                findClass (referenceTypeName (SClass (JNI.toChars cname)))
-                  >>= newGlobalRef
+      klass = unsafeDupablePerformIO $ do
+                lk <- findClass (referenceTypeName (SClass (JNI.toChars cname)))
+                gk <- newGlobalRef lk
+                deleteLocalRef lk
+                return gk
       field = unsafeDupablePerformIO $ getStaticFieldID klass fname (signature retsing)
   case retsing of
     SPrim "boolean" -> unsafeUncoerce . coerce . w2b <$> getStaticBooleanField klass field
@@ -427,51 +447,40 @@
 -- | Inject a value (of primitive or reference type) to a 'JValue'. This
 -- datatype is useful for e.g. passing arguments as a list of homogeneous type.
 -- Synonym for 'coerce'.
-jvalue :: Coercible a ty => a -> JValue
+jvalue :: (ty ~ Ty a, Coercible a) => a -> JValue
 jvalue = coerce
 
 -- | If @ty@ is a reference type, then it should be possible to get an object
 -- from a value.
-jobject :: (Coercible a ty, IsReferenceType ty) => a -> J ty
+jobject :: (ty ~ Ty a, Coercible a, IsReferenceType ty) => a -> J ty
 jobject x
   | JObject jobj <- coerce x = unsafeCast jobj
   | otherwise = error "impossible"
 
--- | Map a Haskell type to the symbolic representation of a Java type.
-type family Interp (a :: k) :: JType
+-- | The 'Interp' type family is used by both 'Reify' and 'Reflect'. In order to
+-- benefit from @-XGeneralizedNewtypeDeriving@ of new instances, we make this an
+-- /associated/ type family instead of a standalone one.
+class (SingI (Interp a), IsReferenceType (Interp a)) => Interpretation (a :: k) where
+  -- | Map a Haskell type to the symbolic representation of a Java type.
+  type Interp a :: JType
 
 -- | Extract a concrete Haskell value from the space of Java objects. That is to
 -- say, unmarshall a Java object to a Haskell value. Unlike coercing, in general
 -- reifying induces allocations and copies.
---
--- Instances of this class /must/ guarantee that the result is managed on the
--- Haskell heap. That is, the Haskell runtime has /global ownership/ of the
--- result.
---
--- WARNING: The default method just creates a global reference to the Java
--- object. Widespread use of this mechanism can cause memory problems since
--- objects in the Java heap cause no pressure on the Haskell garbage collector.
--- If the Haskell's GC is not executed by the time the Java heap is full, the
--- Java code might sporadically fail with OutOfMemory exceptions.
---
-class (SingI (Interp a), IsReferenceType (Interp a))
-      => Reify a where
+class Interpretation a => Reify a where
   reify :: J (Interp a) -> IO a
 
-  default reify :: Coercible a (Interp a) => J (Interp a) -> IO a
-  reify x = (unsafeUncoerce . JObject) <$> newGlobalRef x
+  default reify :: (Coercible a, Interp a ~ Ty a) => J (Interp a) -> IO a
+  reify x = return (unsafeUncoerce (JObject x))
 
 -- | Inject a concrete Haskell value into the space of Java objects. That is to
 -- say, marshall a Haskell value to a Java object. Unlike coercing, in general
 -- reflection induces allocations and copies.
---
--- Instances of this class /must not/ claim global ownership.
-class (SingI (Interp a), IsReferenceType (Interp a))
-      => Reflect a where
+class Interpretation a => Reflect a where
   reflect :: a -> IO (J (Interp a))
 
-  default reflect :: Coercible a (Interp a) => a -> IO (J (Interp a))
-  reflect x = newLocalRef (jobject x)
+  default reflect :: (Coercible a, Interp a ~ Ty a) => a -> IO (J (Interp a))
+  reflect x = return (jobject x)
 
 #if ! (__GLASGOW_HASKELL__ == 800 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1)
 reifyMVector
@@ -506,15 +515,18 @@
 #endif
 
 withStatic [d|
-  type instance Interp () = 'Class "java.lang.Object"
-
-  instance Reify () where
-    reify _ = return ()
+  instance (SingI ty, IsReferenceType ty) => Interpretation (J ty) where type Interp (J ty) = ty
+  instance Interpretation (J ty) => Reify (J ty)
+  instance Interpretation (J ty) => Reflect (J ty)
 
-  instance Reflect () where
-    reflect () = new []
+  -- Ugly work around the fact that java has no equivalent of the 'unit' type:
+  -- We take an arbitrary serializable type to represent it.
+  instance Interpretation () where type Interp () = 'Class "java.lang.Short"
+  instance Reify () where reify _ = return ()
+  instance Reflect () where reflect () = new [JShort 0]
 
-  type instance Interp ByteString = 'Array ('Prim "byte")
+  instance Interpretation ByteString where
+    type Interp ByteString = 'Array ('Prim "byte")
 
   instance Reify ByteString where
     reify jobj = do
@@ -532,7 +544,8 @@
         setByteArrayRegion arr 0 (fromIntegral n) content
         return arr
 
-  type instance Interp Bool = 'Class "java.lang.Boolean"
+  instance Interpretation Bool where
+    type Interp Bool = 'Class "java.lang.Boolean"
 
   instance Reify Bool where
     reify jobj = do
@@ -548,7 +561,8 @@
   instance Reflect Bool where
     reflect x = new [JBoolean (fromIntegral (fromEnum x))]
 
-  type instance Interp CChar = 'Class "java.lang.Byte"
+  instance Interpretation CChar where
+    type Interp CChar = 'Class "java.lang.Byte"
 
   instance Reify CChar where
     reify jobj = do
@@ -563,7 +577,8 @@
   instance Reflect CChar where
     reflect x = Language.Java.new [JByte x]
 
-  type instance Interp Int16 = 'Class "java.lang.Short"
+  instance Interpretation Int16 where
+    type Interp Int16 = 'Class "java.lang.Short"
 
   instance Reify Int16 where
     reify jobj = do
@@ -578,7 +593,8 @@
   instance Reflect Int16 where
     reflect x = new [JShort x]
 
-  type instance Interp Int32 = 'Class "java.lang.Integer"
+  instance Interpretation Int32 where
+    type Interp Int32 = 'Class "java.lang.Integer"
 
   instance Reify Int32 where
     reify jobj = do
@@ -594,7 +610,8 @@
   instance Reflect Int32 where
     reflect x = new [JInt x]
 
-  type instance Interp Int64 = 'Class "java.lang.Long"
+  instance Interpretation Int64 where
+    type Interp Int64 = 'Class "java.lang.Long"
 
   instance Reify Int64 where
     reify jobj = do
@@ -609,7 +626,8 @@
   instance Reflect Int64 where
     reflect x = new [JLong x]
 
-  type instance Interp Word16 = 'Class "java.lang.Character"
+  instance Interpretation Word16 where
+    type Interp Word16 = 'Class "java.lang.Character"
 
   instance Reify Word16 where
     reify jobj = do
@@ -625,7 +643,8 @@
   instance Reflect Word16 where
     reflect x = new [JChar x]
 
-  type instance Interp Double = 'Class "java.lang.Double"
+  instance Interpretation Double where
+    type Interp Double = 'Class "java.lang.Double"
 
   instance Reify Double where
     reify jobj = do
@@ -640,7 +659,8 @@
   instance Reflect Double where
     reflect x = new [JDouble x]
 
-  type instance Interp Float = 'Class "java.lang.Float"
+  instance Interpretation Float where
+    type Interp Float = 'Class "java.lang.Float"
 
   instance Reify Float where
     reify jobj = do
@@ -655,7 +675,8 @@
   instance Reflect Float where
     reflect x = new [JFloat x]
 
-  type instance Interp Text = 'Class "java.lang.String"
+  instance Interpretation Text where
+    type Interp Text = 'Class "java.lang.String"
 
   instance Reify Text where
     reify jobj = do
@@ -673,7 +694,8 @@
 -- Instances can't be compiled on GHC 8.0.1 due to
 -- https://ghc.haskell.org/trac/ghc/ticket/12082.
 #if ! (__GLASGOW_HASKELL__ == 800 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1)
-  type instance Interp (IOVector Int32) = 'Array ('Prim "int")
+  instance Interpretation (IOVector Int32) where
+    type Interp (IOVector Int32) = 'Array ('Prim "int")
 
   instance Reify (IOVector Int32) where
     reify = reifyMVector (getIntArrayElements) (releaseIntArrayElements)
@@ -681,7 +703,8 @@
   instance Reflect (IOVector Int32) where
     reflect = reflectMVector (newIntArray) (setIntArrayRegion)
 
-  type instance Interp (Vector Int32) = 'Array ('Prim "int")
+  instance Interpretation (Vector Int32) where
+    type Interp (Vector Int32) = 'Array ('Prim "int")
 
   instance Reify (Vector Int32) where
     reify = Vector.freeze <=< reify
@@ -689,8 +712,8 @@
   instance Reflect (Vector Int32) where
     reflect = reflect <=< Vector.thaw
 #endif
-
-  type instance Interp [a] = 'Array (Interp a)
+  instance Interpretation a => Interpretation [a] where
+    type Interp [a] = 'Array (Interp a)
 
   instance Reify a => Reify [a] where
     reify jobj = do
